// Un tipo que cuenta lo que le pasa: sirve para contar copias y movimientos.
#ifndef TRACKED_H
#define TRACKED_H

struct Counts {
    long default_built = 0;  // construidos sin argumentos
    long value_built = 0;    // construidos con un valor
    long copy_built = 0;     // construidos como copia
    long move_built = 0;     // construidos por movimiento
    long copies = 0;         // copias: por construcción y por asignación
    long moves = 0;          // movimientos: por construcción y por asignación
    long destroyed = 0;

    // Cuántos objetos están vivos en este momento.
    long alive() const { return default_built + value_built + copy_built + move_built - destroyed; }
};

// Los contadores son static porque new T[n] llama al constructor sin argumentos:
// no hay cómo pasarle a cada objeto un contador propio.
// NoexceptMove dice si el movimiento promete no lanzar excepciones.
template <bool NoexceptMove>
class BasicTracked {
public:
    inline static Counts counts;

    BasicTracked() { counts.default_built++; }
    explicit BasicTracked(int value) : value_(value) { counts.value_built++; }
    ~BasicTracked() { counts.destroyed++; }

    BasicTracked(const BasicTracked& other) : value_(other.value_) {
        counts.copies++;
        counts.copy_built++;
    }
    BasicTracked& operator=(const BasicTracked& other) {
        value_ = other.value_;
        counts.copies++;
        return *this;
    }
    BasicTracked(BasicTracked&& other) noexcept(NoexceptMove) : value_(other.value_) {
        other.value_ = -1;
        counts.moves++;
        counts.move_built++;
    }
    BasicTracked& operator=(BasicTracked&& other) noexcept(NoexceptMove) {
        value_ = other.value_;
        if (this != &other) {
            other.value_ = -1;
        }
        counts.moves++;
        return *this;
    }

    int value() const { return value_; }
    static void reset() { counts = Counts{}; }

private:
    int value_ = 0;
};

using Tracked = BasicTracked<true>;            // su movimiento es noexcept
using TrackedRiskyMove = BasicTracked<false>;  // su movimiento no promete nada

#endif
