zoukankan      html  css  js  c++  java
  • 【原创】C++11:左值和右值(深度分析)

    ——原创,引用请附带博客地址

    2019-12-06 23:42:18 这篇文章分析的还是不行,先暂时放在这以后再更新。

    本篇比较长,需要耐心阅读

    以一个实际问题开始分析

    class Sub{}
    Sub GetInstance(){
    return Sub();
    }
    int main(){
    Sub a=GetInstance();
    ....
    ////////////////////////////////////////////////////////////////////


    【分析】:例子比较简易,主要通过GetInstance方法返回一个Sub实例,在这个过程中,发生的事情如下:

    1. GetInstance内部执行Sub()构造一次,由于是临时变量,在函数结束后被析构。
    2. 返回的临时变量又克隆一份给返回值,这个过程调用一次复制构造函数
    3.  在main函数中,由于赋值又调用了一次复制构造函数

    整个过程执行了三次构造(包括复制构造),最关键的问题在于,在GetInstance()方法中第一次构造的Sub实例在函数结束后被析构,存在的意义只在于被复制构造给返回值。

    又假定,如果Sub对象非常巨大,那么得到实例a的代价非常巨大。整个来看,函数内构造的Sub实例没有太大意义,一个设想是能不能把这个Sub实例保存下来,这样至少节省了一次构造。换句话说,能不能把将要消失的临时对象保存下来,延长它的生命周期?

    左值右值定义

    一般说法,可以取地址的,有名字的是左值,反之,不能取地址,没有名字的是右值。
    比如:a=b+c;
    &a是允许的操作,而&(b+c)不能通过编译,因此a是一个左值,而(b+c)是一个右值。

    C++11中,右值由两个概念组成,一个是将亡值,另一个是纯右值

    纯右值

    纯右值是C++98中标准中右值的概念,主要用于辨识临时变量和一些不跟对象关联的值。
    (*注:不跟对象关联,这段话非常特别,后面再解释)

    比如:非引用返回的函数返回的临时变量值就是纯右值。
    1. 一些运算表达式,比如1+3产生的临时变量值为纯右值。
    2. 不跟对象关联的字面量值,比如2、'c'、true等,也是纯右值。

    将亡值

    将亡值是C++11新增的跟右值引用相关的表达式,这种表达式通常是将被移动的对象(移为他用),比如返回右值引用T &&的函数返回值、std::move的返回值(后面详细解释)

    在C++11的程序中,所有的值必属于左值、将亡值、纯右值三者之一

    右值引用

    一个小例子说明

    int && Add(){
        return 1+2;
    }

    【分析】:方法Add返回一个右值,一般情况下,此处的右值随着方法结束而消失,但是通过右值引用,可以将生命周期延续,此处的 int &&就是右值引用。

    左右值引用的绑定

    一般来说,右值引用不能绑定左值,比如:
    int a=10;
    int &&b=a;
    此处不能通过。

    那么,左值引用能不能绑定右值呢?

    int && Add(){ //右值引用
      return 1+2;
    }
    int &a=Add();

    此处不能通过编译,但是有一个例外,像下面这种情形是可以的。
    const int &a=Add();

    唯独多了一个const,这个const很神奇,下面详细解释一下const

    const

    被const修饰的常量左值引用,可以接纳非常量左值常量左值右值对其进行初始化,是什么意思呢?下面以例子说明

    void Add(const int &a,const int &b){
        std::cout<<a+b<<std::endl;
    }

    【分析】:Add方法的形参是两个const左值引用,因此在调用时分为三种情形:
    1.
    const int a=1;
    const int b=2;
    Add(a,b); //这种属于常量左值入参

    2.
    int a=1;
    int b=2;
    Add(a,b); //这种属于非常量左值入参

    3.
    Add(1,2); //这种属于右值,直接拿右值1,2进行调用

    尤其对于第3种情形,如果Add的形参不是常量左值引用,比如 Add(int a,int b),那么第3种情形不能通过。

    可以看出,const常量左值引用接纳能力非常强,算是“万能”引用类型。因此,const左值引用可以绑定右值
    比如:const int & a=10;完全可行,

    但是,常量左值引用有一个缺点,就是其生存周期中只能是“只读的”。

    函数返回值究竟是什么是类型?

    首先将前面的分析做个阶段总结
    1. 右值引用不能绑定左值
    2. 左值引用绑定右值分为两种情况:
    (1) 如果是非const的左值引用只能绑定左值
    (2) const常量左值引用几乎是万能的,可以绑定常量、非常量左值,或右值
    下面看个例子:
    class Sub{...}

    Sub GetInstance(){
    return Sub();
    }
    Sub &sub=GetInstance(); //可以通过编译

    【分析】:GetInstance()返回的究竟是哪种类型呢?
    如果是右值,根据前面的结论,非常量左值引用是不可以绑定右值的,但结果是编译可以通过。

    这个问题很神奇,现在的结论是:函数返回的都是右值

    回到最前面的右值定义:

    这里提到一句,不跟对象关联的值,但是上述GetInstance方法返回的是类Sub的实例,所以,这里算不算是特殊对待呢?

    将VS的警告级别提升,可以看到如下警告:

    从警告中可以推断出,函数返回的确实是右值,而编译器在这里使用了“非标准扩展初始化”才会使这一句通过。

    结论如下:

    函数返回的是右值,但是和对象有关联的右值,似乎规则放松了一点,可以被非常量左值引用绑定,使用的是【非标准扩展初始化】

    12232

  • 相关阅读:
    设置元素等待.py
    javaWeb服务详解(含源代码,测试通过,注释)
    com.sun.istack.SAXException2: 在对象图中检测到循环。这将产生无限深的 XML
    com.sun.istack.SAXException2: 在对象图中检测到循环。这将产生无限深的 XML
    No services have been found解决方案
    No services have been found解决方案
    JavaMail给QQ邮箱发邮件报错,没有SSL加密
    JavaMail给QQ邮箱发邮件报错,没有SSL加密
    JavaMail给QQ邮箱发邮件报错
    JavaMail给QQ邮箱发邮件报错
  • 原文地址:https://www.cnblogs.com/tinaluo/p/11747562.html
Copyright © 2011-2022 走看看