此部分内容和右值引用重复
    万能引用
    是否看到T&就一定是个左值引用 是
    是否看到T&&就一定是个右值引用 否 — 有的模板代码里 对于类型参数的推导结果可能是引用 — T可能是个引用类型
    假设右值引用是使用&&声明的,那么假设类型声明中出现&& 表示右值引用似乎是合理的。事实并非如此:

    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:

    对于 template foo(T&&) 这样的代码,如果传递过去的实参参数是左值,则编译器对T类型 的自动推导结果是左值引用;如果传递过去的参数是右值,则编译器对T 的自动推导结果是参数的类型本身

    template foo(T&&)
    如果实例化类型 T 为左值引用(或者直接传入一个左值),那 T&& 的结果仍然是左值引用——即 type& && 坍缩成了 type&。

    如果 T 是一个实际类型,那 T&& 的结果自然就是一个右值引用

    如果 T 是右值引用,那 T&& 的结果依然是右值引用

    (下图所讲的实参参数类型 指的是实例化模板参数T所传入的类型是个左引用foo()还是右引用foo() 当然传入就是正常情况 下图所讲的函数形参类型指的是foo(T&&) 还是foo(T&))
    3右值和移动究竟解决了什么问题. 2 万能引用 - 图1
    可以发现当函数形参类型为右引用时 推导后的类型依然保持不变
    foo(T&&) foo((int&&)x)-推导->foo(int&&),foo((int&)x)-推导->foo(int&) 当我们用右值引用作为函数形参,并使用自动推导机制(不显示地给定模板类型) 可以保证推导后的形参类型和传入的实参类型一样,这是个很好的性质(转发引用,万能引用)

    1.万能限定必须是函数模板,可以模板参数是单个,也可以是多个模板参数,形式为T&&
    2.万能引用可以接受左值,也可以接受右值,而不像前面普通函数TestLValueOrRValue一样,必须与左右引用形式对应3.万能引用的T不能被再修饰,否则转为普通右值引用,不能被cv修饰限定4.如果想在模板类中的模板函数使用万能引用,不能使用模板类的参数,否则转为普通右值引用,如下代码样例:

    1. template<class T>
    2. struct Test {
    3. // a参数来源T,源自模板类,不是万能引用,为右值引用
    4. // b参数来源U,源自模板函数,是万能引用
    5. template<class U> // U是函数模板 T是类模板
    6. A(T&& a, U&& b, int c);
    7. }



    1.区分万能引用和右值引用
    例1

    1. Widget&& var1 = someWidget;
    2. auto&& var2 = var1;

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

    例2

    1. std::vector<int> v;
    2. ...
    3. auto&& val = v[0]; // val becomes an lvalue reference (see below)

    val是auto&&—-T&&,T需要被推导,所以它就是一个universal reference。
    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

    虽然T需要推导,但是不是T&&形式。所以param是个右值引用而不是万能引用

    例4

    1. template<typename T>
    2. void f(const T&& param); // “&&” means rvalue reference

    有T&&,但是多了个const。const T&&不是万能引用而是一个右值引用。
    Universal references只以 “T&&”的形式出现!即便是仅仅加一个const限定符都会使得“&&”不再被解释为universal reference

    例5

    1. template<typename MyTemplateParamType>
    2. void f(MyTemplateParamType&& param); // “&&” means universal reference

    不是说非得叫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,即是说把参数转发给另一个函数。