#include #include #include #define USE_MOVE_OPS 1 class array { protected: class rep { public: rep (void) : m_data (new double [0]), m_len (0), m_count (1) { std::cerr << " rep::rep () [DEFAULT CTOR]" << std::endl; } explicit rep (size_t len) : m_data (new double [len]), m_len (len), m_count (1) { std::cerr << " rep::rep (len) [UNINITIALIZED CTOR]" << std::endl; } explicit rep (size_t len, const double& val) : m_data (new double [len]), m_len (len), m_count (1) { std::cerr << " rep::rep (len, val) [FILL CTOR]" << std::endl; std::fill_n (m_data, m_len, val); } rep (const rep& a) : m_data (new double [a.m_len]), m_len (a.m_len), m_count (1) { std::cerr << " rep::rep (const rep&) [COPY CTOR]" << std::endl; std::copy_n (a.m_data, a.m_len, m_data); } #if defined (USE_MOVE_OPS) rep (rep&& a) : m_data (a.m_data), m_len (a.m_len), m_count (a.m_count) { std::cerr << " rep::rep (rep&&) [MOVE CTOR]" << std::endl; a.m_data = nullptr; a.m_len = 0; a.m_count = 0; } #endif rep& operator = (const rep& a) { if (this != &a) { std::cerr << " rep::operator = (const rep&) [COPY ASSIGN]" << std::endl; delete [] m_data; m_data = new double [a.m_len]; m_len = a.m_len; m_count = 1; std::copy_n (a.m_data, a.m_len, m_data); } else std::cerr << " rep::operator = (const rep&) [COPY ASSIGN SAME OBJECT]" << std::endl; return *this; } #if defined (USE_MOVE_OPS) rep& operator = (rep&& a) { if (this != &a) { std::cerr << " rep::operator = (rep&&) [MOVE ASSIGN]" << std::endl; delete [] m_data; m_data = a.m_data; m_len = a.m_len; m_count = a.m_count; a.m_data = nullptr; a.m_len = 0; a.m_count = 0; } else std::cerr << " rep::operator = (rep&&) [MOVE ASSIGN SAME OBJECT]" << std::endl; return *this; } #endif ~rep (void) { std::cerr << " rep::~rep () [DTOR]" << std::endl; delete [] m_data; } size_t numel (void) const { return m_len; } void show (void) const { std::cerr << " rep [" << this << "] " << " length: " << m_len << " count: " << m_count << std::endl; } double *m_data; size_t m_len; int m_count; }; public: array (void) : m_rep (new rep ()) { std::cerr << " array::array () [DEFAULT CTOR]" << std::endl; } explicit array (size_t len) : m_rep (new rep (len)) { std::cerr << " array::array (len) [UNINITIALIZED CTOR]" << std::endl; } array (size_t len, const double& val) : m_rep (new rep (len, val)) { std::cerr << " array::array (len, val) [FILL CTOR]" << std::endl; } array (const array& a) : m_rep (a.m_rep) { std::cerr << " array::array (const rep&) [COPY CTOR]" << std::endl; m_rep->m_count++; } #if defined (USE_MOVE_OPS) array (array&& a) : m_rep (a.m_rep) { std::cerr << " array::array (rep&&) [MOVE CTOR]" << std::endl; a.m_rep = nullptr; } #endif array& operator = (const array& a) { if (this != &a) { std::cerr << " array::operator = (const rep&) [COPY ASSIGN]" << std::endl; if (--m_rep->m_count == 0) delete m_rep; m_rep = a.m_rep; m_rep->m_count++; } else std::cerr << " array::operator = (const rep&) [COPY ASSIGN SAME OBJECT]" << std::endl; return *this; } #if defined (USE_MOVE_OPS) array& operator = (array&& a) { if (this != &a) { std::cerr << " array::operator = (const rep&) [MOVE ASSIGN]" << std::endl; m_rep = a.m_rep; a.m_rep = nullptr; } else std::cerr << " array::operator = (const rep&) [MOVE ASSIGN SAME OBJECT]" << std::endl; return *this; } #endif virtual ~array (void) { if (! m_rep) { std::cerr << " array::~array () [DTOR INVALID REP]" << std::endl; } else if (--m_rep->m_count == 0) { std::cerr << " array::~array () [DTOR COUNT IS ZERO]" << std::endl; delete m_rep; } else std::cerr << " array::~array () [DTOR COUNT IS NOW " << m_rep->m_count << "]" << std::endl; } void show (void) const { if (m_rep) m_rep->show (); } size_t numel (void) const { return m_rep ? m_rep->numel () : 0; } private: typename array::rep *m_rep; }; array foo (size_t len, const double& val) { array tmp (len, val); return tmp; } int main (void) { std::cerr << "\ncreating X1 and X2, copying X1 to X2" << std::endl; array x1 (2, 0.0); array x2; x2 = x1; // No move? Right, x is an lvalue. x2.show (); std::cerr << "done" << std::endl; std::cerr << "\ncreating U, forcing move to U2" << std::endl; array u (1); // Force a move. Note that this will only be useful if we know that // U will not be needed again because after the move, U is in an // invalid state. Note also that this assignment is transformed // into a move constructor, not a move assignment. array u2 = std::move (u); std::cerr << "done" << std::endl; std::cerr << "\ncreating V and V2, forcing move to V2" << std::endl; array v (1); // Force a move. Note that this will only be useful if we know that // V will not be needed again because after the move, V is in an // invalid state. Now we see a move assignment. array v2; v2 = std::move (v); std::cerr << "done" << std::endl; std::cerr << "\ncalling FOO and copying" << std::endl; // Note that the compiler can completely eliminate the copy // constructor here and directly construct Z1 from the temporary // value returned from FOO. array z1 (foo (3, 5.0)); z1.show (); std::cerr << "done" << std::endl; std::cerr << "\ncalling FOO and assigning" << std::endl; // Performed this way, we do see a call to the move assignment // operator because the result of foo is a temporary value. Instead // of copying and deleting, it can be moved. array z2; z2 = foo (3, 5.0); z2.show (); std::cerr << "done" << std::endl; std::cerr << "\nreturning" << std::endl; return 0; }