Advertisement
ascend4nt

Rvalues in move constructors and move-assignment functions

Mar 14th, 2013
155
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 6.89 KB | None | 0 0
  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. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement