Brief view of C++11 move semantics

1. Example

#include <iostream>
using namespace std;

class T
{
public:
    T() { cout <<"T()" <<endl; }
    T(const T &t) { cout <<"T(const T &t)" <<endl; }
    T(T &&t) { cout <<"T(T &&t)" <<endl; }
};

auto foo1(T &&t) -> void
{
    T newOwner1(static_cast<T &&>(t));      // This sentence is equal to 
                                                     // the one below.

    T newOwner2(forward<T>(t));                // More offical expression.
}

auto foo2(T &&t) -> void
{
    T copy(t);                              // Just create a copy of 't'.
                                            // Bacause 't' has a name, it is a
                                            // lvalue.
}

int main()
{
    // Move semantics.
    cout <<"Move semantics:" <<endl;
    foo1( (T()) );                          // '(T())' is the most vexing parse.
                                            // http://www.cnblogs.com/walfud/articles/2391768.html

    cout <<endl;
    // Copy semantics.
    cout <<"Copy semantcis:" <<endl;    
    foo2( (T()) );

    return 0;
}

 

 


2. How to implement move semantics

'static_cast<T &&>(t)': 

  Because 't' has a name, it is not a rvalue, even though it refernces to a temporary object created by 'T()' in 'main'.

  Of course, we want to use move semantics, so we have to remove 't' 's name, and keep the reference type. (about how to keep reference type, please see http://thbecker.net/articles/rvalue_references/section_08.html)

  The clever us use 'static_cast' to remove its name, and type 'T &&' will keep the type unchaned.

 

  So clever right? But not graceful! Let's see a more official statement.

'forward<T>':

template<class S>
S&& forward(typename remove_reference<S>::type& a) noexcept
{
    return static_cast<S&&>(a);
} 

--- comes from http://thbecker.net/articles/rvalue_references/section_08.html

  Yes, you know the secret behind 'forward'.

  'forward' only remove the name of variable, but keep reference type remained.


 3. Solve the rapid increase of overload

class X{};
class Y{};

auto foo(const X &x, const Y &y) -> void;   // None.
auto foo(X &&x, const Y &y) -> void;        // 'X' use move semantics.
auto foo(const X &x, Y &&y) -> void;        // 'Y'...
auto foo(X &&x, Y &&y) -> void;             // Both 'X' & 'y'.

  if there is a function with n paramter, we have to write n2 functions! Terrible...
  Let's ask template for help.

template <typename T, typename U>
auto foo(T &&t, U &&u) -> void;

  This template will do your work.


4. Where should we use move semantics

  Only under three situation should we use move semantcis:

    1. copy contructor
    2. assignment operator
    3. special function. 
      i.   take a memory consumed parameter or need a local copy.
      ii.  edit it.
      iii. return it.

  This guideline is practical. We use move semantcis to shift ownership between variables, you must think did you need move ownership indeed whenever you want it.

  Obviously, we want to obtain data from a temporary object returned from function, which is to be copied. Move semantic copy contructor is needed.

  Assignment operator has the same semantics of copy constructor, so does [yassignment operator.

  For the special function, look at the following code

vector<string> foo();

vector<string> bar(const vector<string> &v)
{
  vector<string> res(v);      // Local copy.
// Edit 'v' and return it. ...
return v;
}


int main()
{
    bar(foo());                    // 'bar()' did a useless copy of temporary variable returned by 'foo()' if no RVO.

    return 0;
}

which take parameter by value (or const reference, but a local temporary object is need as return value), cause a useless copy of temporary variable. Now, we need plunder the data from temporary and shift it to 'bar()'. Thus we avoid huge memory allocation and decallocation.

 

posted @ 2012-08-28 17:42  walfud  阅读(370)  评论(0编辑  收藏  举报