SHARE
TWEET

Rvalues in move constructors and move-assignment functions

ascend4nt Mar 14th, 2013 27 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #include <utility>
  2. #include <iostream>
  3. #include <string>
  4.  
  5. // Rvalues in move constructors and move-assignment functions
  6. // Author: Ascend4nt
  7. // http://ascend4nt.wordpress.com/2013/03/14/rvalues-in-move-constructors-and-move-assignment-functions/
  8.  
  9. using std::cout; using std::endl;
  10.  
  11. template <typename T>
  12. class SuperClass
  13. {
  14. public:    
  15.  
  16.         SuperClass() : m_Str("'a string'"), m_Data(), m_Int(0), m_bValid(true)
  17.         {
  18.                 cout << "SuperClass() empty constructor" << endl;
  19.         }
  20.         ~SuperClass()
  21.         {
  22.                 cout << "~SuperClass() destructor, string value at destruct: " << m_Str << endl;
  23.         }
  24.  
  25.         SuperClass(const SuperClass<T>& copyFrom) : m_Str(copyFrom.m_Str), m_Data(copyFrom.m_Data)
  26.         {
  27.                 cout << "SuperClass COPY-constructor" << endl;
  28.                 m_Int = copyFrom.m_Int; m_bValid = copyFrom.m_bValid;
  29.         }
  30.         SuperClass(SuperClass<T>&& moveFrom) throw()
  31.         // All non-POD data must be std::move()'d
  32.                 : m_Str(std::move(moveFrom.m_Str)), m_Data(std::move(moveFrom.m_Data))
  33.         {
  34.                 cout << "SuperClass MOVE-constructor" << endl;
  35.                 m_Int = std::move(moveFrom.m_Int);     
  36.                 m_bValid = std::move(moveFrom.m_bValid);
  37.         }
  38.  
  39.         SuperClass<T>& operator=(const SuperClass<T>& copyFrom)
  40.         {
  41.                 cout << "SuperClass COPY-Assignment" << endl;
  42.                 // Avoid copies to self
  43.                 if (this != &copyFrom)
  44.                 {
  45.                         assign(copyFrom);
  46.                 }
  47.                 return *this;
  48.         }
  49.         SuperClass<T>& operator=(SuperClass<T>&& moveFrom) throw()
  50.         {
  51.                 cout << "SuperClass MOVE-Assignment" << endl;
  52.                 // Avoid moves to self
  53.                 if (this != &moveFrom)
  54.                 {
  55.                         // Move MUST be used, otherwise assign(Const LValue) version is invoked
  56.                         assign(std::move(moveFrom));
  57.                 }
  58.                 return *this;
  59.         }
  60.  
  61.         void assign(const SuperClass<T>& copyFrom)
  62.         {
  63.                 cout << "SuperClass.assign() - Const LValue version" << endl;
  64.                 // Our data will be overwritten, so its best to free the resources 1st
  65.                 destroy();
  66.                 // Now we can copy
  67.                 m_Str = copyFrom.m_Str;
  68.                 m_Data = copyFrom.m_Data;
  69.                 m_Int = copyFrom.m_Int;
  70.                 m_bValid = copyFrom.m_bValid;
  71.         }
  72.  
  73.         void assign(SuperClass<T>&& moveFrom)
  74.         {
  75.                 cout << "SuperClass.assign() - RValue version" << endl;
  76.         // Our data will be overwritten, so its best to free the resources 1st
  77.         destroy();        
  78.                 // move for non-POD and unknown types:
  79.                 m_Str = std::move(moveFrom.m_Str);
  80.                 m_Data = std::move(moveFrom.m_Data);
  81.                
  82.                 // for POD members we can simply copy-assign, then invalidate the temporary
  83.                 m_Int = moveFrom.m_Int;
  84.                 moveFrom.m_Int = 0;
  85.  
  86.                 m_bValid = moveFrom.m_bValid;
  87.                 moveFrom.m_bValid = false;
  88.         }
  89.  
  90.         void destroy()
  91.         {
  92.                 m_Str.clear();
  93.                 m_bValid = false;              
  94.                 // Since T doesn't have a known 'destroy' method, we can't make assumptions here
  95.                 //m_Data.destroy();
  96.         }
  97.  
  98.         void setInt(int nInt)
  99.         {
  100.                 m_Int = nInt;
  101.         }
  102.  
  103. protected:
  104.         std::string m_Str;
  105.         T m_Data;
  106.         int m_Int;
  107.         bool m_bValid; 
  108. };
  109.  
  110.  
  111. class SubClass : public SuperClass<unsigned>
  112. {
  113. public:
  114.  
  115.         typedef SuperClass<unsigned> Parent;
  116.  
  117.         SubClass() : Parent()
  118.         {
  119.                 cout << "SubClass() empty constructor" << endl;
  120.         }
  121.         ~SubClass()
  122.         {
  123.                 cout << "~SubClass() destructor" << endl;
  124.         }
  125.         SubClass(const SubClass& copyFrom) : Parent(copyFrom)
  126.         {
  127.                 cout << "SubClass COPY-constructor" << endl;
  128.         }
  129.         SubClass(SubClass&& moveFrom) throw()
  130.                 : Parent(std::move(moveFrom))
  131.         {
  132.                 cout << "SubClass MOVE-constructor" << endl;
  133.         }
  134.  
  135.         SubClass& operator=(const SubClass& copyFrom)
  136.         {
  137.                 cout << "SubClass COPY-Assignment" << endl;
  138.                 if (this != &copyFrom)
  139.                 {
  140.                         Parent::assign(copyFrom);
  141.                 }
  142.                 return *this;
  143.         }
  144.         SubClass& operator=(SubClass&& moveFrom) throw()
  145.         {
  146.                 cout << "SubClass MOVE-Assignment" << endl;
  147.                 if (this != &moveFrom)
  148.                 {
  149.                         // Move MUST be used, otherwise assign(Const LValue) version is invoked
  150.                         Parent::assign(std::move(moveFrom));
  151.                 }
  152.                 return *this;
  153.         }
  154. };
  155.  
  156. class SubFailClass : public SuperClass<char>
  157. {
  158. public:
  159.         typedef SuperClass<char> Parent;
  160.  
  161.         SubFailClass() : Parent()
  162.         {
  163.                 cout << "SubFailClass() empty constructor" << endl;
  164.         }
  165.         ~SubFailClass()
  166.         {
  167.                 cout << "~SubFailClass() destructor" << endl;
  168.         }
  169.         SubFailClass(const SubFailClass& copyFrom) : Parent(copyFrom)
  170.         {
  171.                 cout << "SubFailClass COPY-constructor" << endl;
  172.         }
  173.  
  174. // Failure to pass moveFrom to parent correctly
  175.         SubFailClass(SubFailClass&& moveFrom) throw()
  176.         // Correct version = ": Parent(std::move(moveFrom))"
  177.                 : Parent(moveFrom)     
  178.         {
  179.                 cout << "SubFailClass MOVE-constructor" << endl;
  180.         }
  181.  
  182.         SubFailClass& operator=(const SubFailClass& copyFrom)
  183.         {
  184.                 cout << "SubFailClass COPY-Assignment" << endl;
  185.                 if (this != &copyFrom)
  186.                 {
  187.                         Parent::assign(copyFrom);
  188.                 }
  189.                 return *this;
  190.         }
  191. // Failure to pass to correct assign()
  192.         SubFailClass& operator=(SubFailClass&& moveFrom) throw()
  193.         {
  194.                 cout << "SubFailClass MOVE-Assignment" << endl;
  195.                 if (this != &moveFrom)
  196.                 {
  197.                         // Move MUST be used, otherwise assign(Const LValue) version is invoked
  198.                         Parent::assign(moveFrom);
  199.                 }
  200.                 return *this;
  201.         }
  202. };
  203.  
  204.  
  205. SubClass retSub()
  206. {
  207.         cout << "\n- retSub(): returning temporary --\n" << endl;
  208.         auto Obj = SubClass();
  209.         // Perform some operation(s) so that the compiler is less likely to elide the temporary
  210.         Obj.setInt(5);
  211.         return Obj;
  212. }
  213.  
  214. void TestSub()
  215. {
  216.         cout << "\n[[TestSub(): Initializing child object via temp (via retSub())]]" << endl;
  217.         //Move construction: implicit, explicit:
  218.         //auto myObj = retSub();
  219.         auto myObj(retSub());
  220.  
  221.         cout << "\n[[TestSub(): Empty constructor]]\n" << endl;
  222.         SubClass dupObj;
  223.  
  224.         cout << "\n[[TestSub(): Copy-assignment]]\n" << endl;
  225.         dupObj = myObj;
  226.         cout << "\n[[TestSub() exiting, Destructors follow]]\n" << endl;
  227. }
  228.  
  229.  
  230. SuperClass<short> retSuper()
  231. {
  232.         cout << "\n-- retSuper(): returning temporary [copy elision form] --\n" << endl;
  233.         // This line is typically constructed at the return location, rather than creating a temporary
  234.         return SuperClass<short>();
  235. }
  236.  
  237. void TestSuper()
  238. {
  239.         cout << "\n[[TestSuper(): Initializing parent object via temp (via retSuper())]]" << endl;
  240.         auto mySuper = retSuper();
  241.  
  242.         cout << "\n[[TestSuper(): Empty construction]]\n" << endl;
  243.         decltype(retSuper()) dupSuper;
  244.  
  245.         cout << "\n[[TestSuper(): MOVE-assignment]]\n" << endl;
  246.         dupSuper = std::move(mySuper);
  247.  
  248.         cout << "\n[[TestSuper() exiting, Destructors follow]]\n" << endl;
  249. }
  250.  
  251. SubFailClass retSubFail()
  252. {
  253.         cout << "\n-- retSubFail(): returning temporary --\n" << endl;
  254.         auto Obj = SubFailClass();
  255.         // Perform some operation(s) so that the compiler is less likely to elide the temporary
  256.         Obj.setInt(8);
  257.         return Obj;
  258. }
  259.  
  260. void TestSubFail()
  261. {
  262.         cout << "\n[[TestSubFail(): Initializing child object via temp (via retSubFail()) -\n\tNotice Parent COPY-construct error!]]" << endl;
  263.         auto myFail = retSubFail();
  264.  
  265.         cout << "\n[[TestSubFail(): Empty constructor]]\n" << endl;
  266.         SubFailClass dupFail;
  267.  
  268.         cout << "\n[[TestSubFail(): MOVE-assignment - Notice incorrect LValue assign!]]\n" << endl;
  269.         dupFail = std::move(myFail);
  270.  
  271.         cout << "\n[[TestSubFail() exiting, Destructors follow]]\n" << endl;
  272. }
  273.  
  274. int main()
  275. {
  276.         TestSuper();
  277.         TestSub();
  278.         TestSubFail();
  279.         return 0;
  280. }
RAW Paste Data
Top