#include <iostream>
#include <vector>

struct X
{
    X() : id(instances++)
    {
        std::cout << "X" << id << ": construct\n";
    }

    X(std::vector<int>) : id(instances++)
    {
        std::cout << "X" << id << ": construct (with vector)\n";
    }

    X(X const& rhs) : id(instances++)
    {
        std::cout << "X" << id << ": <- " << "X" << rhs.id << ": **copy**\n";
        ++copies;
    }

    X(X&& rhs) : id(instances++)
    {
        std::cout << "X" << id << ": <- " << "X" << rhs.id << ": **move**\n";
        ++moves;
    }

    // This particular test doesn't exercise assignment, but for
    // completeness:
    X& operator=(X const& rhs)
    {
        std::cout << "X" << id << ": <- " << "X" << rhs.id << ": copy-assign\n";
    }

    X& operator=(X&& rhs)
    {
        std::cout << "X" << id << ": <- " << "X" << rhs.id << ": move-assign\n";
    }

    ~X() { std::cout << "X" << id << ": destroy\n"; }

    unsigned id;

    static unsigned copies;
    static unsigned moves;
    static unsigned instances;
};

unsigned X::copies    = 0;
unsigned X::moves     = 0;
unsigned X::instances = 0;

#define CHECK_COPIES( stmt, min, max_copies, max_moves, comment )           \
{                                                                           \
    unsigned const old_copies = X::copies;                                  \
    unsigned const old_moves  = X::moves;                                   \
                                                                            \
    std::cout << "\n" comment "\n" #stmt "\n===========\n";                 \
    {                                                                       \
        stmt;                                                               \
    }                                                                       \
    unsigned const n = X::copies - old_copies;                              \
    unsigned const m = X::moves  - old_moves;                               \
    unsigned const t = n+m;                                                 \
    if (t < min)                                                            \
        std::cout << "*** min is too high or compiler is buggy ***\n";      \
    if (n > max_copies)                                                     \
        std::cout << "*** max_copies is too low or compiler is buggy ***\n";\
    if (m > max_moves)                                                      \
        std::cout << "*** max_moves is too low or compiler is buggy ***\n"; \
                                                                            \
                                                                            \
    std::cout << "-----------\n"                                            \
              << n << "/" << max_copies                                     \
              << " possible copies made\n"                                  \
              << m << "/" << max_moves                                      \
              << " possible moves made\n"                                   \
              << max_copies - t << "/" << max_copies - min                  \
              << " possible elisions performed\n\n";                        \
                                                                            \
    if (t > min)                                                            \
        std::cout << "*** " << t - min                                      \
                  << " possible elisions missed! ***\n";                    \
}

struct trace
{
    trace(char const* name)
        : name(name)
    {
        std::cout << "->: " << name << "\n";
    }

    ~trace()
    {
        std::cout << "<-: " << name << "\n";
    }

    char const* name;
};

std::vector<int> vector()
{
    std::vector<int> ret;
    return ret;
}

void sink(X a)
{
    trace t("sink");
}

X nrvo_source()
{
    trace t("nrvo_source");
    X a;
    return a;
}

X urvo_source()
{
    trace t("urvo_source");
    return X();
}

X nrvo_source_vector()
{
    trace t("nrvo_source_vector");
    X a( vector() );
    return a;
}

X urvo_source_vector()
{
    trace t("urvo_source_vector");
    return X( vector() );
}

X identity(X a)
{
    trace t("identity");
    return a;
}

X lvalue_;
X& lvalue()
{
    return lvalue_;
}
typedef X rvalue;

int main()
{
    // Double parens prevent "most vexing parse"
    CHECK_COPIES( X a(( lvalue() )), 1, 1, 0, "Direct initialization from lvalue");
    CHECK_COPIES( X a(( rvalue() )), 0, 1, 1, "Direct initialization from rvalue");
    CHECK_COPIES( X a(( rvalue(vector()) )), 0, 1, 1, "Direct initialization from rvalue, constructed with vector");

    CHECK_COPIES( X a = lvalue(), 1, 1, 0, "Copy initialization from lvalue" );
    CHECK_COPIES( X a = rvalue(), 0, 1, 1, "Copy initialization from rvalue" );
    CHECK_COPIES( X a = rvalue(vector()), 0, 1, 1, "Copy initialization from rvalue, constructed with vector" );

    CHECK_COPIES( sink( lvalue() ), 1, 1, 0, "Pass lvalue by value" );
    CHECK_COPIES( sink( rvalue() ), 0, 1, 1, "Pass rvalue by value" );
    CHECK_COPIES( sink( rvalue(vector()) ), 0, 1, 1, "Pass rvalue by value, constructed with vector" );

    CHECK_COPIES( nrvo_source(), 0, 1, 1, "Named return value optimization (NRVO)" );
    CHECK_COPIES( nrvo_source_vector(), 0, 1, 1, "Named return value optimization (NRVO), constructed with vector" );

    CHECK_COPIES( urvo_source(), 0, 1, 1, "Unnamed return value optimization (URVO)" );
    CHECK_COPIES( urvo_source_vector(), 0, 1, 1, "Unnamed return value optimization (URVO), constructed with vector" );

    // Just to prove these things compose properly
    CHECK_COPIES( X a(urvo_source()), 0, 2, 2, "Return value used as ctor arg" );

    // Expect to miss one possible elision here
    CHECK_COPIES( identity( rvalue() ), 0, 2, 2, "Return rvalue passed by value" );
}