此部分内容和右值引用重复
    2.引用折叠
    引用折叠规则:
    X& + & => X&
    X&& + & => X&
    X& + && => X&
    X&& + && => X&&
    函数模板参数推导规则(右值引用参数部分):
    当函数模板的模板参数为T而函数形参为T&&(右值引用)时适用本规则。
    若实参为左值(U,U&,U&&的有名左值 等)则认为其类型为U& ,则模板参数 T 应推导为引用类型 U& 。
    (根据引用折叠规则, U& + && => U&, 而T&& ≡ U&,故T ≡ U& )
    若实参为右值(U(),move(左值) 亡值,static_cast左值 亡值等)则认为其类型为U&& ,则模板参数 T 应推导为非引用类型 U 。
    (根据引用折叠规则, U或U&& + && => U&&, 而T&& ≡ U&&,故T ≡ U,这里强制规定T ≡ U )
    std::remove_reference为C++0x标准库中的元函数,其功能为去除类型中的引用。
    std::remove_reference::type ≡ U
    std::remove_reference::type ≡ U
    std::remove_reference::type ≡ U
    以下语法形式将把表达式 t 转换为T类型的亡值(右值的一种)staticcast(t)—-_返回的是一个亡值
    非引用类型的转型表达式,如static_cast(x),(int)1是纯右值
    static_cast(x)返回的是个亡值 无名右值引用
    static_cast(x) 引用类型的转型表达式是
    staticcast<__新类型__> 如果新类型是对象类型的右值引用则返回亡值(右值的一种),新类型是左值引用(或者是某个函数类型的右值引用很奇怪也很少见)则返回左值,除去这两种情况其他新类型cast_返回的都是纯右值。

    对于对象的引用必须是左值(常量引用除外)const引用能够绑定到临时对象, 并将临时对象的生命周期由”创建临时对象的完整表达式”提升至”绑定到的const引用超出作用域”。

    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);


    3右值和移动究竟解决了什么问题. 3 引用折叠,完美转发 - 图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&
    5. 当一个变量本身的类型是引用类型时会出一些问题


    3.c++中遇到模板类型需要推导的情况
    例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

    例2 auto场景

    1. Widget&& var1 = someWidget; // var1 is of type Widget&& (no use of auto here)
    2. auto&&是万引用 传入左值var1,发生自动推导和引用折叠。auto&&被推导为左值引用Widget&
    3. auto&& var2 = var1; // var2 is of type Widget& (see below)


    例3 typedef场景

    1. template<typename T>
    2. class Widget {
    3. typedef T& LvalueRefType;
    4. typedef T&& RvalueRefType;
    5. ...
    6. };
    7. int main() {
    8. Widget<int&> w;
    9. }

    注意万能引用是万能引用,引用折叠是引用折叠。并不是说引用折叠是万能引用专属的特性。上面这个例子就只发生了引用折叠
    除了右值引用的右值引用会折叠为右值引用,所有其他种类的”引用的引用” (i.e., 组合当中含有lvalue reference) 都会折叠为 lvalue reference。

    推导LvalueRefType,因为传入的T类型实例化为int&,这里T&出现的就是左值引用的左值引用(int& &),这里出现的T&&就是左值引用的右值引用(int& &&) 根据规则都会发生引用折叠变为左值引用int&。—-T==int&(就是这么实例化的) T&== LvalueRefType==int&(因为左值引用的左值引用 发生了引用折叠 变回了左值引用) T&&==RvalueRefType==int&(因为左值引用的右值引用 发生了引用折叠 变回了左值引用)

    如果我们只用这些typedef,一样会发生引用折叠
    void f(Widget::LvalueRefType&& param);
    对typedef扩展void f(int& && param);—-左值引用的右值引用发生引用折叠变为
    void f(int& param);

    void f(Widget::RvalueRefType&& param);—左值引用(RvalueRefType在上面的推导中也变为int&)的右值引用发生引用折叠变为
    void f(int& param);
    例4 decltype场景

    和模板和 auto 一样,decltype 对表达式进行类型推导时候可能会返回 T 或者 T&,然后decltype 会应用 C++11 的引用折叠规则。
    decltype 的类型推导规则其实和模板或者 auto 的类型推导不一样。这里的细节过于晦涩
    在这里先注意一个点
    decltype 对一个具名的、非引用类型的变量,会推导为类型 T ,在相同条件下,模板和 auto 却会推导出 T&。
    decltype((变量))双层括号的结果永远是引用类型,而decltype(变量)的结果只有当变量本身是一个引用时才会返回引用类型
    decltype 进行类型推导只依赖于 decltype括号内的表达式; 用来对变量进行初始化的表达式的类型(如果有的话)会被忽略。—-即不看用于初始化的实参,只着眼于decltype括号内的表达式

    如下例

    1. Widget w1, w2;
    2. auto&& v1 = w1;

    auto&&是万能引用,传入w1是左值 推导T为Widget &,Widget& &&发生引用折叠变为Widget&。

    1. decltype(w1)&& v2 = w2;

    w1为具名非引用变量,推导为T为类型Widget ,Widget &&正常 是个右值引用,decltype推导类型并不会关心由于初始化v2变量的实参w2的类型,只关心decltype括号里的内容—-因为右值引用只能绑定右值,而这里给了个左值所以编译出错

    注意C++的类型推导(auto decltype typedef 万能引用模板 引用折叠)是在编译时进行,编译完就明确知道调用的函数具体的所有细节
    4.完美转发

    1. void foo(const shape&)
    2. {
    3. puts("foo(const shape&)");
    4. }
    5. void foo(shape&&)
    6. {
    7. puts("foo(shape&&)");
    8. }
    9. void bar(const shape& s)
    10. {
    11. puts("bar(const shape&)");
    12. foo(s);
    13. }
    14. void bar(shape&& s)
    15. {
    16. puts("bar(shape&&)");
    17. foo(s);
    18. }
    19. int main()
    20. {
    21. bar(circle());
    22. }


    输出为:bar(shape&&)foo(const shape&)

    bar(circle()); 首先circle()是个右值所以会调用右值版本的bar,
    在bar中

    1. void bar(shape&& s)
    2. {
    3. puts("bar(shape&&)");
    4. foo(s);//传入的实参是个右值付给形参s(有名是左值), 一旦赋值完成右值就变为有名 不再是右值了,因为s是左值 所以调用的是void foo(const shape&)
    5. }

    输出为:
    bar(shape&&)
    foo(const shape&)
    形参为匿名对象赋名导致其成为左值 就是不完美转交的根本原因。

    1. //如果我们想bar中调用foo的右值引用版本,需要将s通过move强制变为右值
    2. foo(std::move(s));
    3. //或者通过static_cast强制将s类型变为右值引用类型
    4. foo(static_cast<shape&&>(s));


    但实际上恨过标准库中的函数是不知道参数类型的,但是依然保持了参数的值类别,左值依然是左值,右值依然是右值。 通过forward实现完美转发

    1. template <typename T>
    2. void bar(T&& s)
    3. {
    4. foo(std::forward<T>(s));
    5. //通过forward保证s传入是左/右值保持为左/右值 通过完美转发保证调用正确的函数版本
    6. }
    1. circle temp;
    2. bar(temp);//左值 传入左值类型s foo(const shape&)
    3. bar(circle());//右值 foo(shape&&)

    注意我们在调用bar时并没有指定模板参数bar,而是让编译器自行推导,这是ok的,当我们没有指定模板参数时编译器会自动根据传入实参类型自动推导模板类型。自动推导的规则就是下图
    3右值和移动究竟解决了什么问题. 3 引用折叠,完美转发 - 图2

    在 T 是模板参数时,T&& 的作用主要是保持值类别进行转发,它有个名字就叫“转发引用”(forwarding reference)。因为既可以用左值引用,也可以是右值引用,它也曾经被叫做“万能引用”(universal reference)。