假设右值引用是使用&&声明的,那么假设类型声明中出现&& 表示右值引用似乎是合理的。事实并非如此:

    1. Widget&& var1 = someWidget; // here, “&&” means rvalue reference
    2. auto&& var2 = var1; // here, “&&” does not mean rvalue reference
    3. template<typename T>
    4. void f(std::vector<T>&& param); // here, “&&” means rvalue reference
    5. template<typename T>
    6. void f(T&& param); // here, “&&”does not mean rvalue reference


    类型声明当中的“&&”有的时候意味着rvalue reference,但有的时候意味着rvalue reference 或者 lvalue reference (两种都有可能)。因此,源代码当中出现的 “&&” 有可能是 “&” 的意思,即是说,语法上看着像rvalue reference (“&&”),但实际上却代表着一个lvalue reference (“&”)。在这种情况下,此种引用比lvalue references 或者 rvalue references都要来的更灵活。
    Rvalue references&&只能绑定到右值上,lvalue references&除了可以绑定到左值上,特殊的常左值引用const…&可以绑定到右值上。

    声明中带 “&&” 的,可能是lvalue references 或者 rvalue references 可以绑定到任何东西上。这种引用灵活也忒灵活了,值得单独给它们起个名字。我称它们为 universal references(万能引用或转发引用、通用引用)。

    到底 “&&” 什么时候才意味着一个universal reference(万能引用)呢(即,代码当中的“&&”实际上可能是 “&”)
    如果一个变量或者参数被声明为T&&,其中T是被推导的类型(T是模板参数 或者 auto 或者使用typedef和decltype的时候也可能会出现),那这个变量或者参数就是一个universal reference(万能引用)
    在实践当中,几乎所有的universal references都是函数模板的参数。因为auto声明的变量的类型推导规则本质上和模板是一样的,所以使用auto的时候你也可能得到一个universal references。

    和所有的引用一样,你必须对universal references进行初始化,而且正是universal reference的initializer决定了它到底代表的是lvalue reference 还是 rvalue reference:


    从结果来看,万能引用的结果如下
    如果用来初始化universal reference的表达式是一个左值,那么universal reference就变成lvalue reference。
    如果用来初始化universal reference的表达式是一个右值,那么universal reference就变成rvalue reference。

    从中间发生的事来看,中间发生如下
    万能引用+引用坍缩(万能引用的推导过程中会发生引用折叠)
    对于 template foo(T&&)这样的代码。如果传递过去的实参是左值,T 的推导结果(万能引用的推导结果)是左值引用(实参类别的左值引用 比如传入实参是int&类型的左值—则T被推导为 int&中类型 int的左值引用 int&), int& &&会发生引用折叠 那 T&& 的结果仍然是左值引用——即 int& && 坍缩成了int&
    如果传递过去的实参是右值,T 的推导结果(万能引用的推导结果)是参数的类型本身(是实参类型本身 比如传入的实参是int型的右值—则T被推导为 int中类型 int)。 int&&不会发生引用折叠 T&&的结果自然就是一个右值引用。
    例子

    1. template<typename T>
    2. void f(T&& param);
    3. //T是被推导的类型,并且param被申明为T&&所以param变量的表达式值类别为universal references(万能引用)
    4. 此时
    5. int a;//左值
    6. f(a);//传入左值 那T&& 万能引用就变为了int& 左值引用
    7. f(int(1));//传入字面量右值 那T&&万能引用就变为了int&&右值引用

    区分万能引用
    例1

    1. Widget&& var1 = someWidget;
    2. auto&& var2 = var1;
    3. //因为var1是有名的所以var1是一个左值,var2的类型声明是auto&&---T&&,
    4. //T需要被推导,所以它就是一个universal reference。
    5. //并且var2是被 左值var1初始化,所以var2就被自动推导为一个左值引用(Widget&)。
    6. //当一个universal reference开始被lvalue初始化的时候,
    7. //var2就变成了lvalue reference


    例2

    1. std::vector<int> v;
    2. ...
    3. auto&& val = v[0]; // val becomes an lvalue reference (see below)
    4. //val是auto&&---T&&,T需要被推导,所以它就是一个universal reference。
    5. //v[0]可被取地址所以是个左值, 所以val 就被自动推导为一个左值引用(int&)。


    只有在发生类型推导的时候 “&&” 才代表 universal reference 吗。如果没有类型推导,就没有universal reference。这种时候,类型声明当中的“&&”总是代表着rvalue reference。

    T&& 万能引用
    template
    void f(T&& param); // deduced parameter type ⇒ type deduction;

    Widget&& 右值引用
    void f(Widget&& param);
    例3

    1. template<typename T>
    2. void f(std::vector<T>&& param); // “&&” means rvalue reference
    3. //虽然T需要推导,但是不是T&&形式。所以param是个右值引用而不是万能引用


    例4

    1. template<typename T>
    2. void f(const T&& param); // “&&” means rvalue reference
    3. //有T&&,但是多了个const。const T&&不是万能引用而是一个右值引用。

    Universal references只以 “T&&”的形式出现!即便是仅仅加一个const限定符都会使得“&&”不再被解释为universal reference

    例5

    1. template<typename MyTemplateParamType>
    2. void f(MyTemplateParamType&& param); // “&&” means universal reference
    3. //不是说非得叫T,MyTemplateParamType&&--param是个万能引用


    例6

    1. template <class T, class Allocator = allocator<T> >
    2. class vector {
    3. public:
    4. ...
    5. void push_back(T&& x); // fully specified parameter type ⇒ no type deduction;
    6. ... // && ≡ rvalue reference
    7. };

    这里, T 是模板参数, 并且push_back接受一个``T&&, 但是这个参数却不是universal reference。
    看push_back函数在类外是怎么声明的,就很清楚了。
    template
    void vector::push_back(T&& x);
    push_back不能离开std::vector这个类而独立存在。但如果我们有了一个叫做std::vector的类,我们就已经知道了T是什么东西,那就没必要推导T。——这个T是用于实例化整个类,而不是只用于实例化这个函数 所以这里的T&&是右值引用!!!

    例7—-6的具体例子

    1. Widget makeWidget(); // factory function for Widget
    2. std::vector<Widget> vw;
    3. ...
    4. Widget w;
    5. vw.push_back(makeWidget()); // create Widget from factory, add it to vw

    代码中对 push_back 的使用会让编译器实例化类 std::vector 相应的函数。这个push_back 的声明看起来像这样:
    void std::vector::push_back(Widget&& x); x是右值引用类型
    一旦我们知道了类是 std::vector(实例化了类)push_back的参数类型就完全确定了: 就是Widget&&。这里完全不需要进行任何的类型推导。

    例8

    1. template <class T, class Allocator = allocator<T> >
    2. class vector {
    3. public:
    4. ...
    5. template <class... Args>
    6. void emplace_back(Args&&... args); // deduced parameter types ⇒ type deduction;
    7. ... // && ≡ universal references
    8. };

    emplace_back 看起来需要多个参数(因为Args和args的声明当中都有…),但重点是每一个参数的类型都需要进行推导。函数的模板参数 Args 和类的模板参数T无关(Args是函数专用的模板 仅仅知道了T依旧不知道Args,所以Args依旧需要被推导,所以Args&&为万能引用),所以即使我知道这个类具体是什么(知道T),但是不知道Args,Args依旧需要推导 所以Args&&是万能引用。

    template
    void std::vector::emplace_back(Args&&… args);
    实例化了T为Widget但是Args没有被实例化,Args依旧需要被推导所以Args&&是万能引用。


    一个绑定到universal reference上的对象可能具有 lvalueness 或者 rvalueness,正是因为有这种二义性,所以催生了std::forward:希望在传递参数的时候,可以保存参数原来的lvalueness 或 rvalueness,即是说把参数转发给另一个函数。

    引用折叠

    1. template<typename T>
    2. void f(T&& param);//T&&为万能引用
    3. ...
    4. int x;
    5. ...
    6. f(10); // invoke f on rvalue
    7. f(x); // invoke f on lvalue


    因为万能引用的特性,如果用来初始化万能引用的表达式是个左值,那万能引用就变为左值引用。如果用来初始化万能引用的表达式是个右值,那么万能引用就变为右值引用

    10是字面右值,万能引用自动推导为void f(int&& param);—-T被推导为int // f instantiated from rvalue
    类型T的 rvalues 被推导为 T—-10是int型的rvalues,则万能引用会将T推导为int

    ok没问题
    x是左值,c++11以前 万能引用自动推导为
    void f(int& && param); // initial instantiation of f with lvalue
    类型T的lvalues被推导为T&—x是int型的lvalues,则万能引用会将T推导为int&

    c++11以前不行,在c++11以后推出引用折叠解决了这个问题
    c++11 f(x);万能引用自动推导为 void f(int& param);

    右值引用详细说明1 - 图1

    引用折叠只有两条规则(就是上面那张图)
    一个右值引用的右值引用会变成 (“折叠为”) 一个 rvalue reference.
    所有其他种类的”引用的引用” (i.e., 组合当中含有lvalue reference) 都会折叠为 lvalue reference。引用折叠的解释 也是下面这个例子
    对于 template foo(T&&)这样的代码。

    如果传递过去的实参是左值(不管是类型是左值引用,or 普通左值,or 右值引用类型的左值),T 的推导结果(万能引用的推导结果)是左值引用(比如传入实参是X&,X&&类型的有名左值,X普通非引用类型 —则T都被推导为 其中类型 X的左值引用 X&),那 T&& 的结果仍然是左值引用——即 X& && 坍缩成了X&

    如果传递过去的实参是右值(纯右值临时对象or move后的左值—亡值 or static_cast<右值引用>—亡值 ),T 的推导结果(万能引用的推导结果)是参数的类型本身(是实参类型本身 比如传入的实参是X型的右值 or move(左值) or static_cast 后的左值 —则T被推导为 其中类型X)。那 T&& 的结果自然就是一个右值引用X&&。

    1. int x;
    2. ...
    3. int&& r1 = 10; // r1’s type is int&&
    4. int& r2 = x; // r2’s type is int&

    当一个变量本身的类型是引用类型时会出一些问题
    例1

    1. template<typename T>
    2. void f(T &&param) {
    3. static_assert(std::is_lvalue_reference<T>::value, "T& is lvalue reference");
    4. cout << "T& is lvalue reference" << endl;
    5. }
    6. int main() {
    7. int x;
    8. int &&r1 = 10;
    9. int &r2 = x;
    10. f(r1);
    11. f(r2);
    12. }


    r1 和r2 的类型都被推导为 int&
    因为r1和r2都是有名的,都是左值,传入万能引用中进行推导得到的就是左值引用类型的param