zoukankan      html  css  js  c++  java
  • C++11左值引用和右值引用

    转载:https://www.cnblogs.com/golaxy/p/9212897.html

    C++11的左值引用与右值引用总结

     概念

    1、&与&&
      对于在C++中,大家对于符号“ & ”的第一映像是引用和取地址,对符号“ && ”的第一映像是逻辑与。但是在C++11中我们会看到下方这种表示方法:

    int &&k=i+k;
    1.1
      对于第一眼见到上述的表达方式时,估计会一脸懵逼。下面对此做一个简述。
      引用 & 是一个类型变量的别名。
      通常使用 “ & ”是作为普通的左值引用,左值是可以放在赋值号 “ = ”的左边的值。
      &&是右值引用,右值是只能放在 “ = ” 右边的值,右值没有一个 “ 变量 ”名字。如:

    int && k =i+j;
    1.2
      i+j 返回的值可以视做是放在一个临时存放内存空间里的,这个空间并不能用来赋值,你不能些i+j=5等; && 可以把这种临时的值和存在变量里的值区分开来。
      右值短暂,右值只能绑定到临时对象。所引用的对象将要销毁或对象没有其他用户。
      初始化时右值引用一定要用一个右值表达式绑定,初始化之后,可以用左值表达式或修改右值引用的所引用临时对象的值。
      右值引用是对临时对象的一种引用,它是在初始化时完成引用的,但是右值引用不代表引用临时对象后,就不能改变右值引用所引用对象的值。仍然可以在初始化后改变临时对象的值。

      对于引用类型,可以用于它所引用对象类型的可以用的地方(把他当成普通变量),只不过用到的值是它所引用的对象的值,还可以用于移动构造和赋值函数的地方

    在C++11中,区别表达式是左值或右值可以做这样的总结:当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。左值有持久的状态,而右值要么是字面常量,要么是在表达式求值过程中创建的对象,即左值持久,右值短暂

    以上的定义来自于C++Primer(5th)第121页和第471页。可能令人困惑,看了接下来的例子就明白了

    [cpp] view plain copy
     
    1. int &&rr1 = 42;//正确,字面常量是右值  
    2. int &&rr2 = rr1;//错误,表达式rr1是左值  

    特性中的应用

        decltype关键字

            使用关键字decltype的时候,左值和右值有所不同。如果表达式的求值结果是左值,decltype作用于该表达式(不是变量)得到一个引用类型,例如,假定p的类型是int*,因为解引用运算符生成左值,所以decltype(*p)的结果是int&。另一方面,因为取地址运算符生成右值,所以decltype(&p)的结果是int**,也就是说,结果是一个指向整型指针的指针。

        lambda表达式

            lambda表达式是形如auto f = [ ]{ }的函数。

            lambda可指定其捕获列表的类型,[&]表示捕获列表采用隐式引用捕获方式lambda函数体中所使用的来自所在函数的实体都采用引用方式使用,[=]表示采用值捕获方式。

    [cpp] view plain copy
     
    1. /*f1的sz是隐式值捕获,f2的sz是隐式引用捕获  
    2. auto f1 = [=](const string &s)  
    3.       { return s.size() >=sz; }  
    4. auto f2 = [&](const string &s)  
    5.       { return s.size() >=sz; }  

        拷贝控制

        移动构造函数使用右值引用的形参

    [cpp] view plain copy
     
    1. class Foo  
    2. {  
    3.    //移动构造函数  
    4.    Foo(Foo&&);  
    5. };  

        引用折叠和实参推断

        实参推断,按照实参和形参匹配的规则。
        引用折叠:
               通常情况下,我们不能将一个右值引用绑定到一个左值上,但是C++语言在正常绑定规则外定义了两个例外规则,允许这种绑定。
        第一个例外规则影响右值引用的参数推断如何进行:当我们将一个左值传递给函数的右值引用参数,且此右值引用指向模板类型参数(如T&&),编译器推断模板类型参数为实参的左值引用类型。
    [cpp] view plain copy
     
    1. //编译器推断T类型是int&(左值引用),而非int  
    2. template <typename T> void f(T&&);  
    3. int i = 0;  
    4. f(i);  

        引用折叠:在所有情况下(除了一个例外),引用会折叠成一个普通的左值引用类型。

        

    [html] view plain copy
     
    1. 即:对于一个给定类型X  
    2. <ol>  
    3. <li>X& & ,X& && ,X&& &都折叠成类型X&<li>  
    4. <li>类型X&& &&折叠成X&&<li>  
    5. </ol>  
        注:引用折叠只能应用于间接创建的引用的引用,如类型别名或模板参数。

    这两个规则导致了两个重要结果:

        如果一个函数参数是一个指向模板类型参数的右值引用(如T&&),则他可以被绑定到一个左值。且

        如果实参是一个左值,则推断出的模板实参类型将是一个左值引用,且函数参数将被实例化为一个普通的左值引用参数(T&)

    也暗示了:我们可以将任意类型的实参传递给T&&类型的函数参数。

        函数重载

        重载函数定义两个版本,一个版本接受一个指向const的左值引用,第二个版本接受一个指定非const的右值引用。即可接受所有可转换成当前类型的对象
        
    [cpp] view plain copy
     
    1. void Foo(const X&);  
    2. void Foo(X&&);  

       我们知道,非常量可以初始化一个底层const,反过来却不行,所以第一种版本可以接收任何能转换成类型X的任何对象。第二种版本只可以传递非const右值,由于精确匹配规则,传递非const右值时,会调用第二种版本,尽管第一种版本也可以接受。

       一般来说,我们不需要为函数操作定义接受一个const X&&或是一个普通的X&参数的版本。当我们希望从实参“窃取‘数据时,通常传递一个右值引用。为了达到这一目的,实参不能使const的。类似的,从一个对象进行拷贝的操作,不应该改变该对象,因此,通常不需要定义一个接受一个普通的X&参数的版本。

  • 相关阅读:
    gcc链接g++编译生成的静态库和动态库的makefile示例
    gcc将多个静态库链接成一个静态库
    linux c redirect 重定向
    iOS开发如何提高
    致PHP路上的“年轻人”
    显示系统时间--带有秒数
    在 Linux 中使用日志来排错
    程序员必备:技术面试准备手册
    你的Java代码对JIT编译友好么?
    悟空:用Go语言编写的全文搜索引擎
  • 原文地址:https://www.cnblogs.com/tsh292278/p/10436674.html
Copyright © 2011-2022 走看看