// Qué queda en un std::vector si una excepción interrumpe el crecimiento.
#include <iostream>
#include <stdexcept>
#include <vector>

// Se puede copiar, y su movimiento no es noexcept: al crecer, el vector lo copia.
// Lanza una excepción cuando copies_left llega a 0.
class FailsOnCopy {
public:
    inline static int copies_left = 2;
    explicit FailsOnCopy(int value) : value_(value) {}
    FailsOnCopy(const FailsOnCopy& other) : value_(other.value_) {
        if (copies_left == 0) {
            throw std::runtime_error("falló una copia");
        }
        copies_left--;
    }
    FailsOnCopy(FailsOnCopy&& other) : value_(other.value_) { other.value_ = -1; }
    int value() const { return value_; }

private:
    int value_;
};

// Solo se puede mover, y el movimiento no es noexcept. Lanza una excepción cuando
// moves_left llega a 0.
class FailsOnMove {
public:
    inline static int moves_left = 100;
    explicit FailsOnMove(int value) : value_(value) {}
    FailsOnMove(const FailsOnMove&) = delete;
    FailsOnMove(FailsOnMove&& other) : value_(other.value_) {
        if (moves_left == 0) {
            throw std::runtime_error("falló un movimiento");
        }
        moves_left--;
        other.value_ = -1;
    }
    int value() const { return value_; }

private:
    int value_;
};

template <typename T>
void print_values(const std::vector<T>& v) {
    std::cout << "size " << v.size() << ", capacity " << v.capacity() << ", valores:";
    for (const T& item : v) {
        std::cout << " " << item.value();
    }
    std::cout << "\n";
}

int main() {
    std::vector<FailsOnCopy> copied;
    copied.reserve(4);
    for (int i = 1; i <= 4; i++) {
        copied.push_back(FailsOnCopy(i));
    }
    try {
        copied.push_back(FailsOnCopy(5));
    } catch (const std::runtime_error& error) {
        std::cout << "excepción: " << error.what() << "\n";
    }
    print_values(copied);

    std::vector<FailsOnMove> moved;
    moved.reserve(4);
    for (int i = 1; i <= 4; i++) {
        moved.push_back(FailsOnMove(i));
    }
    FailsOnMove extra(5);
    FailsOnMove::moves_left = 3;
    try {
        moved.push_back(std::move(extra));
    } catch (const std::runtime_error& error) {
        std::cout << "excepción: " << error.what() << "\n";
    }
    print_values(moved);
    return 0;
}
