// Al crecer, std::vector mueve sus elementos solo si mover no puede lanzar una excepción.
#include <iostream>
#include <type_traits>
#include <utility>
#include <vector>

struct Counts {
    int copies = 0;
    int moves = 0;
};

class SafeMove {
public:
    explicit SafeMove(Counts& counts) : counts_(&counts) {}
    SafeMove(const SafeMove& other) : counts_(other.counts_) {
        counts_->copies++;
    }
    SafeMove(SafeMove&& other) noexcept : counts_(other.counts_) {
        counts_->moves++;
    }

private:
    Counts* counts_;
};

class RiskyMove {
public:
    explicit RiskyMove(Counts& counts) : counts_(&counts) {}
    RiskyMove(const RiskyMove& other) : counts_(other.counts_) {
        counts_->copies++;
    }
    RiskyMove(RiskyMove&& other) : counts_(other.counts_) {
        counts_->moves++;
    }

private:
    Counts* counts_;
};

template <typename T>
void grow(const char* name) {
    Counts counts;
    std::vector<T> items;
    items.reserve(1000);
    for (int i = 0; i < 1000; i++) {
        items.emplace_back(counts);
    }
    std::cout << name << " nothrow=" << std::is_nothrow_move_constructible_v<T>
              << " antes de crecer: copias=" << counts.copies << " movimientos=" << counts.moves << "\n";
    items.reserve(2000);
    std::cout << name << " después de crecer: copias=" << counts.copies
              << " movimientos=" << counts.moves << "\n";
}

int main() {
    grow<SafeMove>("SafeMove");
    grow<RiskyMove>("RiskyMove");
    return 0;
}
