// ¿std::vector copia o mueve al crecer? Depende de si el movimiento es noexcept.
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>

#include "tracked.h"

template <typename T>
void insert_sixteen(const char* name) {
    std::vector<T> v;
    T::reset();
    for (int i = 0; i < 16; i++) {
        v.push_back(T(i));
    }
    std::cout << name << ": capacity " << v.capacity() << ", copias " << T::counts.copies
              << ", movimientos " << T::counts.moves << "\n";
}

// El compilador escribe su movimiento, y es noexcept porque el de std::string lo es.
struct Student {
    std::string name;
};

// Declarar un destructor apaga el movimiento automático: "mover" termina copiando.
struct StudentWithDestructor {
    std::string name;
    ~StudentWithDestructor() {}
};

// Promete noexcept al construir, pero no al asignar.
struct Mismatched {
    Mismatched() = default;
    Mismatched(const Mismatched&) = default;
    Mismatched(Mismatched&&) noexcept {}
    Mismatched& operator=(const Mismatched&) = default;
    Mismatched& operator=(Mismatched&&) { return *this; }
};

int main() {
    insert_sixteen<Tracked>("mover con noexcept");
    insert_sixteen<TrackedRiskyMove>("mover sin noexcept");
    std::cout << std::boolalpha;
    std::cout << "std::string: " << std::is_nothrow_move_constructible_v<std::string> << "\n";
    std::cout << "Student: " << std::is_nothrow_move_constructible_v<Student> << "\n";
    std::cout << "StudentWithDestructor: "
              << std::is_nothrow_move_constructible_v<StudentWithDestructor> << "\n";
    std::cout << "Mismatched, al construir: " << std::is_nothrow_move_constructible_v<Mismatched>
              << ", al asignar: " << std::is_nothrow_move_assignable_v<Mismatched> << "\n";
    return 0;
}
