Using rvalue references (C++11)

In C++11, you can overload functions based on the value categories of arguments and similarly have lvalueness detected by template argument deduction. You can also have an rvalue bound to an rvalue reference and modify the rvalue through the reference. This enables a programming technique with which you can reuse the resources of expiring objects and therefore improve the performance of your libraries, especially if you use generic code with class types, for example, template data structures. Additionally, the value category can be considered when writing a forwarding function.

Move semantics

When you want to optimize the use of temporary values, you can use a move operation in what is known as destructive copying. Consider the following string concatenation and assignment:

std::string a, b, c;
c = a + b;

In this program, the compiler first stores the result of a + b in an internal temporary variable, that is, an rvalue.

The signature of a normal copy assignment operator is as follows:
string& operator = (const string&)
With this copy assignment operator, the assignment consists of the following steps:
  1. Copy the temporary variable into c using a deep-copy operation.
  2. Discard the temporary variable.
Deep copying the temporary variable into c is not efficient because the temporary variable is discarded at the next step.

To avoid the needless duplication of the temporary variable, you can implement an assignment operator that moves the variable instead of copying the variable. That is, the argument of the operator is modified by the operation. A move operation is faster because it is done through pointer manipulation, but it requires a reference through which the source variable can be manipulated. However, a + b is a temporary value, which is not easily differentiated from a const-qualified value in C++ before C++11 for the purposes of overload resolution.

With rvalue references, you can create a move assignment operator as follows:
string& operator= (string&&)
With this move assignment operator, the memory allocated for the underlying C-style string in the result of a + b is assigned to c. Therefore, it is not necessary to allocate new memory to hold the underlying string in c and to copy the contents to the new memory.
The following code can be an implementation of the string move assignment operator:
string& string::operator=(string&& str)
{
  // The named rvalue reference str acts like an lvalue
  std::swap(_capacity, str._capacity);
  std::swap(_length, str._length);
  
  // char* _str points to a character array and is a
  // member variable of the string class
  std::swap(_str, str._str);  
  return *this;
}
However, in this implementation, the memory originally held by the string being assigned to is not freed until str is destroyed. The following implementation that uses a local variable is more memory efficient:
string& string::operator=(string&& parm_str)
{
  // The named rvalue reference parm_str acts like an lvalue
  string sink_str; 
  std::swap(sink_str, parm_str);
  std::swap(*this, sink_str);
  return *this;
}
In a similar manner, the following program is a possible implementation of a string concatenation operator:
string operator+(string&& a, const string& b)
{
  return std::move(a+=b); 
}
Note: The std::move function only casts the result of a+=b to an rvalue reference, without moving anything. The return value is constructed using a move constructor because the expression std::move(a+=b) is an rvalue. The relationship between a move constructor and a copy constructor is analogous to the relationship between a move assignment operator and a copy assignment operator.

Perfect forwarding

The std::forward function is a helper template, much like std::move. It returns a reference to its function argument, with the resulting value category determined by the template type argument. In an instantiation of a forwarding function template, the value category of an argument is encoded as part of the deduced type for the related template type parameter. The deduced type is passed to the std::forward function.

The wrapper function in the following example is a forwarding function template that forwards to the do_work function. Use std::forward in forwarding functions on the calls to the target functions. The following example also uses the decltype and trailing return type features to produce a forwarding function that forwards to one of the do_work functions. Calling the wrapper function with any argument results in a call to a do_work function if a suitable overload function exists. Extra temporaries are not created and overload resolution on the forwarding call resolves to the same overload as it would if the do_work function were called directly.
struct s1 *do_work(const int&);               // #1 
struct s2 *do_work(const double&);            // #2 
struct s3 *do_work(int&&);                    // #3 
struct s4 *do_work(double&&);                 // #4 
template <typename T> auto wrapper(T && a)-> 
   decltype(do_work(std::forward<T>(*static_cast<typename std::remove_reference<T>
   ::type*>(0)))) 
{    
   return do_work(std::forward<T>(a)); 
}  
template <typename T> void tPtr(T *t);   
int main() 
{   
   int x;    
   double y;      
   tPtr<s1>(wrapper(x));     // calls #1  
   tPtr<s2>(wrapper(y));     // calls #2  
   tPtr<s3>(wrapper(0));     // calls #3  
   tPtr<s4>(wrapper(1.0));   // calls #4  
} 


Voice your opinion on getting help information Ask IBM compiler experts a technical question in the IBM XL compilers forum Reach out to us