#include <iostream>
 
struct X
{
    X() : id(instances++)
    {
        std::cout << "X" << id << ": construct\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"; }
 
    X& operator+=( const X& rhs ) {
        std::cout << "X" << id << ": += " << "X" << rhs.id << ": add-assign\n";
        return *this;
    }

    unsigned id;
 
    static unsigned copies;
    static unsigned moves;
    static unsigned instances;
};
 
X operator+( const X& lhs, const X& rhs ) {
    std::cout << "X" << lhs.id << ": +  " << "X" << rhs.id << ": add\n";
    X rv = lhs;
    rv += rhs;
    return rv;
}

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";                    \
}
 
X lvalue_;
X& lvalue()
{
    return lvalue_;
}
typedef X rvalue;
 
int main()
{
    CHECK_COPIES( lvalue() + lvalue(), 1, 2, 1, "lvalue + lvalue" );
    CHECK_COPIES( rvalue() + lvalue(), 1, 2, 1, "rvalue + lvalue" );
    CHECK_COPIES( lvalue() + rvalue(), 1, 2, 1, "lvalue + rvalue" );
}
