C ++ 11 rvalue Reference & perfect forwarding

Source: Internet
Author: User
Document directory
  • STD: Move
  • Function Template
  • Reference collapsing rule
  • Deduction
  • STD: Forward
  • Explictly specifiec template parameter
Overview right-value reference is a new feature in C ++ 11 to solve the copy performance problem of large objects and PASS Parameters. Such as T &, where T is the referenced type. The left and right values of lvalue and rvalue can be expressed in C language as they appear on both sides of the expression, but in C ++, the situation becomes more complicated because of the introduction of class. It can be summarized as follows:
  1. The left value can be obtained using the & operator. Note that the address cannot be obtained for a temporary object because it is easy to cause problems.
  2. The left value must have a name.
  3. The right value is not the left value.
The left and right values are only the attributes of the expression. Another concept needs to be understood as the type. The type and the left and right values are not a concept and will be explained immediately. Let's take a few examples to understand the concept of left and right values.
int f();int i;int& g();int&& h();&f; // f is lvalue, &f returns to pointer to the functionf(); // f() is rvalue, as f returns a int by valuei;  // i is lvalueg(); // g() is lvalue, as f returns a lvalue reference to inth(); // h() is rvalue, as f returns a rvalue reference to intvoid f(int&& i){    // i is a rvalue reference, but i is a lvalue as named rvalue reference is lvalue}

Note the return value of a function. A function call usually returns the left value when the return value is referenced by the Left value (see n3242 5.2.2/10, the following is a standard text for your reference ).

5.2.2/10a function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function
Type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
Note that the reference to the right value of a name is the left value, that is, the situation given by the last one (see n3242 5/6. The following standard text is provided for reference ).
5/6 in general, the effect of this rule is that named rvalue references are treated as lvalues and unnamed rvalue
References to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether
Named or not
Reference

The left value reference is like T &, while the right value reference is like T &, and we know that the right value reference can be bound to the right value, can we bind a constant with the right value? Because constants cannot be modified, but T & is not reference to const, is it true?

The answer is yes. See the following example:

#include <iostream>using namespace std;int main(){    int&& rri = 5;    rri = 4;    cout << rri << endl;    // error    // char const*&& rrcc = "hello";    // *rrcc = '1';}

After running in G ++ 4.7.3, we can find that the RRI value is 4.

This involves the initialization of reference. The standard stipulates that for the use of the right value to initialize a right value reference, you can create a copy, that is, here 5 will be saved in an object, so here we modify this object. The following (Part) is described in 8.5.3/5 of the standard ):

8.5.3/5

-Otherwise, the reference shall be an lvalue reference to a non-volatile const Type (I. e., cv1 shall be
Const), or the reference shall be an rvalue reference.

If the initializer exrepssion is xvalue or...

-Otherwise, a temporary of type "cv1 T1" is created and initialized from the initializer expression
Using the rules for a non-reference copy-initialization

Let's look at the Code in the following comments. Since the rrcc type is reference to pointer to const char, an error occurred while assigning values to Char const.

Move constructor:
class foo{public:    foo(foo&& f)        : m_s(f.m_s) // m_s(move(f.m_s))    {    }private:    string m_s;};

Here, Foo (FOO &) is the move ctor. Since F is a reference to the right value, we think that we can directly call the move ctor of string without any processing. This is incorrect. As a result, only the copy ctor of string is called.

The reason is very simple. As we have mentioned above, for a named rvalue reference, it is an lvalue. Since rvalue cannot be bound to lvalue, lvalue can only be bound to the copy ctor of string. Our willingness is to move rather than copy, so here we need to use the STD: Move function of the standard library to convert the left value to the right value reference. For an unamed rvalue reference, it is a right value, so that you can call the string move ctor. STD: movestd: move is a simple task. It only converts the left value to the right value reference through static_cast, that is
static_cast<T&&>(v)

Of course, the specific implementation will be complicated, but please continue. Perfect forwardingperfect forwarding must solve the problem of explosive function overload. Because we have moved ctor, we will obviously declare the parameter as T & when declaring the function parameter; otherwise, if we still use t const & to declare it, then we cannot move the ctor, which makes no sense. However, the problem is that the user may pass in a right value or a left value, so we can reload:

void f(foo const& a);void f(foo&& a);

However, if there are N parameters when the number of parameters changes, we need to overload 2 ^ n functions to solve the problem. Therefore, perfect Forwarding is introduced.

When using perfect forwarding, the function template must be referenced with the function template and the right value, that is
void g(int const&);void g(int&&);template<typename T>void f(T&& v){    g(forward<T>(v));}

Here, the standard library function forward completes type forwarding. The left and right values of the type passed to G are the attributes passed by the user.

Note that when and only when the parameter is T &, the perfect forwarding will be triggered and standard text will be quoted
14.8.2.1/3if P is a CV-qualified type, the top level CV-qualifiers of P's type are ignored for Type deduction. if P is a reference type, the type referred to by P is used for type deduction. if P is an rvalue reference to a CV-unqualified template parameter and
The argument is an lvalue, the type "lvalue reference to a" is used in place of a for Type deduction.
For more information, see. Reference collapsing rule we know that the reference referenced in C ++ is invalid, so the following rules are introduced in the standard,
  1. T & = T &
  2. T & = T &
  3. T & = T &
  4. T & = T &&

It means that when a type, such as T &, the final type is T &. It can be seen that only when T &, the type is the right value reference, and in other cases, the left value reference.

Before explaining the principles of forwarding, deduction first describes several terms used in template derivation. P is the parameter type of the function template defined in the table, A is the type provided by the user when the function template is called. The purpose of derivation is to parse t to match P and. Deduced a is the type of P after conversion (see standard 14.8.2.1). transformed a is the type obtained after transformation when P is a specific condition. Back to the previous perfect forwarding explanation, let's take a look at how function template F derives the template parameter T. The following is an example of the standard:
Template <class T> int f(T&&);template <class T> int g(const T&&);int i;int n1 = f(i); // calls f<int&>(int&)int n2 = f(0); // calls f<int>(int&&)int n3 = g(i); // error: would call g<int>(const int&&), which               // would bind an rvalue reference to an lvalue

Let's take a look at f (I) calls. According to 14.8.2.1/3, we have

  1. P = T &, because P is referenced by the right value, deduced a = T
  2. Since P is a reference of the right value, and I is the left value, and I is of the int type, transformed a = Int &
  3. Deduced a = transformed a => T = Int &

Therefore, we can substitute T = Int & into F to obtain F <Int &> (Int &). According to reference collapsing rule, we can conclude that the parameter type of F is Int &, and the value is left, which is what we want.

Let's look at the call of F (0). We have
  1. P = T &, because P is referenced by the right value, deduced a = T
  2. 0 is the right value, so transformed a = int
  3. Deduced a = transformed a => T = int

So the final signature of F is F <int> (Int &), and the parameter type is right value reference.

So far, the Left and Right Attributes of user parameters are retained. For the last example, you can deduce it by yourself. STD: forward: the implementation is provided here. Forward actually has two reloads, but here we only care about one of the reloads that reference the left value, because we know that the name parameter is the left value, therefore, the left value must be passed to forward.
template<typename T>T&& forward(std::remove_reference<T>::type& v){    return static_cast<T&&>(v);}

It is also a function template. Suppose we specify T = Int &, then there will be:

  1. Remove_reference <t >:: type & = Int &
  2. Static_cast <Int &> (v) = static_cast <Int &> (V)
  3. Return Int & = Int &

The reference collapsing rule is used again here. we specify T as Int &, And the return value is also Int &, perfect !!! Forwarding.

Suppose we specify T = int (here we can also specify T = Int &, but this is usually not the case ),
  1. Remove_reference <t>: Type & = Int &, here we still reference the left value. That's right. Do you still remember that the value we passed to forward is also the left value? Although it is a reference of the right value.
  2. Static_cast <Int &> (v) = static_cast <Int &> (V)
  3. Return Int & = Int &&

Perfect !!!.

So far, we have completed the perfect forwarding and passed the parameter to another function without changing the parameter type. Some readers of the explictly specifiec template parameter may have noticed that the template parameter T is explicitly specified when forward is called. Why? Let's take a look at another forward definition:
template<typename T>T&& forward(T& v){    return static_cast<T&&>(v);}

We can still push and export the data. Here we can also achieve perfect Forwarding (some readers may think that the object whose parameter is const cannot be forwarded. This is not the case. We are all in F (T &&) internal use, so this problem does not exist, you can try to deduce it yourself ). However, if the template is automatically deduced without specifying T, assume that F is:

template<typename T>void f(T&& v){    g(forward(v));}

Assume that we pass in an int with the left value, the type of V is Int &, and the type of V with the forward value is converted to int (5.5 according to the standard) before derivation ), so we can export the forward T = int,

Finally, forward will return Int &&. If the problem occurs, the left value is changed to the right value. The reason why the standard explicitly sets T is to prevent problems caused by automatic template derivation. If you are interested in this document, you can refer to the following documents, which I have benefited a lot from. Here I just wrote an excerpt:
  1. N3242, C ++ standard draft, updated version, Google.
  2. C ++ rvalue reference explained
  3. Universal Reference in Accu overload 111

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.