ae34ffae   
   From: kmccarty@googlemail.com   
      
   Hello,   
      
   I'm trying to understand a lot about the new stuff in C++ 2011 myself,   
   so please take my answers below with a grain of salt.   
      
   On Oct 2, 4:23 pm, fmatthew5876 wrote:   
   > Suppose I have this:   
   >   
   > class Matrix4 {   
   > public:   
   > //stuff   
   > private:   
   > float _m[16];   
   > };   
   >   
   > Matrix4 operator+(Matrix4 l, const Matrix4& r) {   
   > l += r;   
   > return l;   
   > }   
   >   
   > Is there any possible scenario where I would want to also create   
   > a version of operator+ that uses rvalue references? i.e.   
   >   
   > Matrix4 operator+(Matrix4 l, const Matrix4&& r) {   
   > l+=r;   
      
   Here I think you mean "l += std::move(r);" otherwise operator+= will   
   get r by const reference (presumably) rather than by rvalue   
   reference. That won't make any difference in this case, though, as   
   you fear. (And I'm not sure one can pass a const reference into   
   std::move().) But one can do better, see below.   
      
      
   > return l;   
   >   
   > }   
   >   
   > I understand rvalue references are desirable in most cases because we   
   > want to use move constructors instead of making unnecessary copies.   
   > But in this particular case, the matrix class directly contains its   
   > data instead of doing an allocation so there is no move constructor.   
   >   
   > To put it more succinctly, is there ever a case when the first operator+   
   > will create a copy for the second argument where the second operator+   
   > would not?   
      
   Not as written, because as you have it, r is a dead end ... its   
   lifetime as a temporary ends inside this function. It looks like what   
   you really hoped to do was take *l* as an rvalue reference. Then it   
   can be re-used by also *returning* an rvalue reference, which means no   
   copying happens at the function return point, until you finally put   
   the result into an lvalue in calling code:   
      
   Matrix4 && operator + (Matrix4 && l, const Matrix4 & r)   
   { l += r; return std::move(l); }   
      
   (Now, most compilers implement named return value optimization, so you   
   might not see any copies even if 'l' was both taken and returned by   
   value. But this function definition takes away that uncertainty.)   
      
   Since matrix addition (and, fortunately, floating-point addition) is   
   symmetric, you'd probably want to implement both cases:   
      
   Matrix4 && operator + (const Matrix4 & l, Matrix4 && r)   
   { r += l; return std::move(r); }   
      
   And I think you also need to implement the double-rvalue version   
   explicitly, or the compiler may complain about the ambiguity if both   
   inputs are temporaries?   
      
   Matrix4 && operator + (Matrix4 && l, Matrix4 && r)   
   { l += r; return std::move(l); /* choose one randomly */ }   
      
   Finally, your original version is still needed in case both input   
   matrices are lvalues. Again, my apologies as I'm not sure whether   
   your original signature will be ambiguous with respect to the above   
   other three, so I rewrite it here explicitly with const references:   
      
   Matrix4 && operator + (const Matrix4 & l, const Matrix4 & r)   
   { return Matrix4(l) + r; /* call first rvalue ref version */ }   
      
      
   If I understand how all this works, the following code (assuming a, b,   
   c, d are all Matrix4's) should produce only one temporary copy, the   
   one explicitly written out in the last version of the operator above;   
   in addition to the unavoidable copy (though it might be elided by the   
   compiler?) from the temporary result of the entire RHS into the lvalue   
   'result'.   
      
   Matrix4 a, b, c, d;   
   // set a, b, c, d...   
      
   Matrix4 result = a + b + c + d;   
      
   But if you parenthesize terms as follows, there will be two   
   temporaries created, so just having move-enabled code is still not a   
   panacea. One does still need to think consciously about when   
   temporaries will appear:   
      
   Matrix4 result2 = (a + b) + (c + d);   
      
   And one can still rewrite the code at a slight clarity loss to prevent   
   any temporaries at all, bypassing all the rvalue-ref enabled code I   
   just wrote:   
      
   Matrix4 result3{a}; result3 += b; result3 += c; result3 += d;   
      
   Happy to be corrected, as always.   
      
   - Kevin B. McCarty   
      
      
   --   
    [ See http://www.gotw.ca/resources/clcm.htm for info about ]   
    [ comp.lang.c++.moderated. First time posters: Do this! ]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|