1. Reference folding rules

  1. If you indirectly create a reference to a reference, the reference will be “collapsed” (template T type before space).
    • X&&, X&&, X&&Are folded intoX&
    • X&& &&Fold forX&&
  2. An rvalue reference refers to a template type parameter when passing an lvalue to a parameter(T&&)When, the compiler concludes that the template parameter type is an lvalue reference to the argument
template<typename T> 
void f(T&&); 

int i = 42;
f(i) Void f
      
       (int&)
      &>
Copy the code
  1. An lvalue cannot be implicitly converted to an rvalue reference, but it can be passedstatic_castExplicitly converts an lvalue to an rvalue

2. Template derivation

The rules for deriving template types are quite complex. Here is a brief description. For example:

template <typename T>
void f(ParamType param);

f(expr);
Copy the code

The above example is a generic example of a function template, where T is derived from the parameters of f function and ParamType is derived from T. T and ParamType may or may not be equal, because ParamType is modifiable. ParamType is classified into the following types:

  1. ParamTypeIs the value type: because it is value passing, soexprAll modifier features of theconst, references,volatileEtc., are ignored.
template<typename T>
void f(T param); // param is passed for values
f(expr);

int x = 2;
const int cx = x;
const int& rx = x;
int *pp = &x;      
f(x); // T : int, param : int
f(cx); // T : int, param : int
f(rx); // T : int , param : int
f(pp); // T is an int*
Copy the code
  1. ParamTypePointer or reference but not universal reference:ParamTypeIs a pointer or reference, the reference property is ignored in the derivation
template <typename T>
void func(T& param);
int x = 10;         / / x is int
int & rr = x;       // rr is int &
const int cx = x;   // cx is const int
const int& rx = x;  // rx is const int &
int *pp = &x;      
func(x);            / / T for the int
func(cx);           // T is const int
func(rx);           // T is const int
func(rr);           / / T for the int
func(pp);           // T is an int*
// When ParamType is a pointer or reference, the reference property is ignored in the derivation.


template<typename T>
void f(const T& param);
int x = 1;
const int cx = x;
const int &rx = x;

f(x); // T is of type int and param is const int&
f(cx); // T is of type int and param is const int&
f(rx); // T is of type int and param is const int&
// When ParamType is a pointer or reference, the reference property is ignored in the derivation.
// In the same way, since param is already const, the const attribute is also ignored in the derivation.

template<typename T>
void f(T* param);
int x = 1;
const int *px = &x;
f(&x); // T is of type int, param is of type int*
f(px);  // T is const int, param is const int*
Copy the code
  1. ParamTypeIs a generic reference type

template<typename T>
void f(T&& param);
f(expr);
// If expr is an lvalue, then both T and paramType are inferred as lvalue references
// If expr is an rvalue, proceed normally
int x = 2;
const int cx = x;
const int& rx = x;

f(x); // x is an lvalue, T is of type int&, and param is int&
f(cx); cx : lvalue, T : const int&, param: const int&
f(rx); rx : lvalue, T:const int&, param: const int&
f(2);2: rvalue, T: int, param : int &&
Copy the code
  1. paramIs the most complete type, inheriting the type declared in the parameterCr (const and Reference)And the arguments are always brought incr. But there are two exceptions:
  • Generic reference when a parameter (T&&Called a generic reference when used as a template parameter),paramDerived as an lvalue reference or an rvalue reference, depending on the type of argument.
  • When the parameter is not a reference, the argument is passed to the parameter as a value, removing allcrThe modifier.
  1. TIs contained incrModifier, depending onparamIs declared in the parameter. namelyTDoes not duplicate the declared modifier in the parameter.

3. typename remove_reference<T>::type&&type

Before C++11, there were three types of class members: member function, member variable, and static member. But since C++11, there has been a new type of member called type member. Type members, like static members, belong to classes rather than objects and are accessed as static members.

template <typename T>
struct remove_reference{
    typedef T type;  // Define the type alias of T as type
};

template <typename T>
struct remove_reference<T&> // Lvalue reference
{
    typedef T type;
}

template <typename T>
struct remove_reference<T&&> // Rvalue reference
{
   typedef T type;
}
Copy the code

We can see from the above code that after remove_reference processing, the reference to T is removed. T = int&&; T = int&&; T = int&&; T = int&&; T = int;

4. std::moveandstd::forwardparsing

4.1. std::moveparsing

Move is defined in the standard library as follows:

template<typename T>
typename remove_reference<T>::type && move(T&& t)
{
    return static_cast<typename remove_reference<T>::type &&>(t);
}
Copy the code

The argument T&& to move is an rvalue reference rule to a template type parameter. By reference folding, this parameter can match any type of argument, so move can pass an lvalue or an rvalue.

Example 4.4.1.

STD ::move(string(“hello”))

  • First, deduce the rules from the template, exactlyTThe type ofstring ;
  • typenameremove_reference<T>::typeAs the result of thestring ;
  • moveThe parameter type of the function isstring&&;
  • static_cast<string&&>(t).tIs alreadystring&&So the type conversion does nothing, returnsstring&&;

strings1(“hello”); std::move(s1); Call resolution:

  • First, deduce the rules according to the template, determineTThe type ofstring&;
  • typenameremove_reference<T>::typeAs the result of thestring
  • moveThe parameter type of the function isstring&&&, after the reference is foldedstring&;
  • static_cast<string&&>(t).tstring&, afterstatic_castAnd then convert tostring&&To return tostring&&;

In addition to making inferences about parameters, a move returns an rvalue reference that is essentially static_cast

. So the following two calls are equivalent, STD ::move is a syntactic sugar.
&&>

void func(int&& a)
{
    cout << a << endl;
}

int a = 6;
func(std::move(a));

int b = 10;
func(static_cast<int&&>(b));   
Copy the code

4.2. std::forwardparsing

template<typename T> 
T&& forward(typename remove_reference<T>::type& param) 
{
    return static_cast<T&&>(param);
}
Copy the code

A static_cast<> is essentially the same as a move

5. Generic references

Since we can receive an lvalue, right can receive an rvalue. For example, input parameters for move. There are two ways to generalize types:

  • typename remove_reference<T>::type &&Template method
  • Auto & autoIt’s actually in the templateT
#include <iostream>
template<typename T>
void f(T&& param){
    std::cout << "the value is "<< param << std::endl;
}
int main(int argc, char *argv[]){

    int a = 123;
    auto && b = 5;   // A generic reference that can accept an rvalue

    int && c = a;    // Error, rvalue reference, cannot receive lvalue

    auto && d = a;   // A generic reference that can accept an lvalue

    const auto && e = a; // error, adding const is no longer a generic reference

    func(a);         // A generic reference that can accept an lvalue
    func(10);        // A generic reference that can accept an rvalue
}
Copy the code

6. References

C++11/C++14 rvalue reference in C++11 perfect forwarding