• [转载]非常量引用的初始值必须为左值的问题


    首先,先看下面一个例子:

    eg.1

    #include<</span>iostream>
    usingnamespace std;


    int main(){
    int i =2;
    double &r =i;
    return 0;
    }



    gcc error: invalid initialization of reference of type 'double&' from expression of type 'int'
    如果改成 const double &r =i;没有问题。
    难道这里的i不是左值?

    程序改成 :

    eg.2

    int main(){
    double i =2;
    double &r =i;
    return 0;
    }

    没有错误
    难道这里的i又是左值啦?

     

    其实:

    const double &r = i; 
    由于类型不匹配,实际相当于:
    const double inner_tmp = (double)i;  //这里就产生了一个临时变量
    const double &r = inner_tmp;
    临时的中间变量都是const,所有没有const的引用会失败。

     

    左值与右值的区分

     

    首先理解什么是右值引用下面是百度百科给出的解释):

           右值引用(及其支持的Move语意和完美转发)是C++0x将要加入的最重大语言特性之一,这点从该特性的提案在C++ - State of the Evolution列表上高居榜首也可以看得出来。从实践角度讲,它能够完美解决C++中长久以来为人所诟病的临时对象效率问题。从语言本身讲,它健全了C++中的引用类型在左值右值方面的缺陷。从库设计者的角度讲,它给库设计者又带来了一把利器。从库使用者的角度讲,不动一兵一卒便可以获得“免费的”效率提升…   在标准C++语言中,临时量(术语为右值,因其出现在赋值表达式的右边)可以被传给函数,但只能被接受为const &类型。这样函数便无法区分传给const &的是真实的右值还是常规变量。而且,由于类型为const &,函数也无法改变所传对象的值。C++0x将增加一种名为右值引用的新的引用类型,记作typename &&。这种类型可以被接受为非const值,从而允许改变其值。

     

     区分左值与右值:

           C++ 11中引入的一个非常重要的概念就是右值引用。理解右值引用是学习“移动语义”(move semantics)的基础。而要理解右值引用,就必须先区分左值与右值。
           对左值和右值的一个最常见的误解是:等号左边的就是左值,等号右边的就是右值。

           左值和右值都是针对表达式而言的,左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时就不再存在的临时对象。一个区分左值与右值的便捷方法是:看能不能对表达式取地址,如果能,则为左值,否则为右值。下面给出一些例子来进行说明。 

    int a = 10;
    int b = 20;
    int* pFlag = &a;
    vector<<span style="color: rgb(0, 0, 255); ">int> vctTemp;
    vctTemp.push_back(1);
    string str1 = "hello ";
    string str2 = "world";
    const int &m = 1;

    请问,a,b, a+b, a++, ++a, pFlag, *pFlag, vctTemp[0], 100, string("hello"), str1, str1+str2, m分别是左值还是右值?

    [转载]非常量引用的初始值必须为左值的问题

    什么是左值引用:
           区分清楚了左值与右值,我们再来看看左值引用。左值引用根据其修饰符的不同,可以分为非常量左值引用(eg.1 double &r =i;)和常量左值引用(eg.1 const double &r =i;)。
           非常量左值引用只能绑定到非常量左值,不能绑定到常量左值、非常量右值和常量右值。如果允许绑定到常量左值和常量右值,则非常量左值引用可以用于修改常量左值和常量右值,这明显违反了其常量的含义(eg.1中就是出现了非常量左值引用绑定到常量右值的情况)。如果允许绑定到非常量右值,则会导致非常危险的情况出现,因为非常量右值是一个临时对象,非常量左值引用可能会使用一个已经被销毁了的临时对象。
           常量左值引用可以绑定到所有类型的值,包括非常量左值、常量左值、非常量右值和常量右值。

     

    为什么要区分非常量右值引用:

           可以看出,使用左值引用时,我们无法区分出绑定的是否是非常量右值的情况。那么,为什么要对非常量右值进行区分呢,区分出来了又有什么好处呢?这就牵涉到C++中一个著名的性能问题——拷贝临时对象。考虑下面的代码: 

    vector<<span style="color: rgb(0, 0, 255); ">int> GetAllScores()
    {
    vector<<span style="color: rgb(0, 0, 255); ">int> vctTemp;
    vctTemp.push_back(90);
    vctTemp.push_back(95);
    returnvctTemp;
    }

           当使用vector vctScore = GetAllScores()进行初始化时,实际上调用了三次构造函数。尽管有些编译器可以采用RVO(Return Value Optimization)来进行优化,但优化工作只在某些特定条件下才能进行。可以看到,上面很普通的一个函数调用,由于存在临时对象的拷贝,导致了额外的两次拷贝构造函数和析构函数的开销。当然,我们也可以修改函数的形式为void GetAllScores(vector &vctScore),但这并不一定就是我们需要的形式。另外,考虑下面字符串的连接操作:

    strings1("hello");
    strings = s1 + "a"+ "b"+ "c"+ "d"+ "e";

           在对s进行初始化时,会产生大量的临时对象,并涉及到大量字符串的拷贝操作,这显然会影响程序的效率和性能。怎么解决这个问题呢?如果我们能确定某个值是一个非常量右值(或者是一个以后不会再使用的左值),则我们在进行临时对象的拷贝时,可以不用拷贝实际的数据,而只是“窃取”指向实际数据的指针(类似于STL中的auto_ptr,会转移所有权)。C++ 11中引入的右值引用正好可用于标识一个非常量右值。C++ 11中用&表示左值引用,用&&表示右值引用,如:

    int&&a = 10; 

           右值引用根据其修饰符的不同,也可以分为非常量右值引用和常量右值引用。
           非常量右值引用只能绑定到非常量右值,不能绑定到非常量左值、常量左值和常量右值(VS2010 beta版中可以绑定到非常量左值和常量左值,但正式版中为了安全起见,已不允许)。如果允许绑定到非常量左值,则可能会错误地窃取一个持久对象的数据,而这是非常危险的;如果允许绑定到常量左值和常量右值,则非常量右值引用可以用于修改常量左值和常量右值,这明显违反了其常量的含义。
           常量右值引用可以绑定到非常量右值和常量右值,不能绑定到非常量左值和常量左值(理由同上)。可以看出,使用左值引用时,我们无法区分出绑定的是否是非常量

  • 相关阅读:
    javascript执行机制
    PhpStorm常用的一些快捷键
    php值传递和引用传递的区别是什么?
    echo与print,var_dump()和print_r()的区别
    PHP错误类型及屏蔽方法
    工作中常用的正则表达式
    Linux环境变量配置文件/etc/profile、/etc/bashrc、~/.bashrc的区别以及一些隐藏文件的作用
    log4j配置不生效
    Java执行CMD命令
    jar的解压与打包
  • 原文地址:https://www.cnblogs.com/zhoug2020/p/7821125.html
Copyright © 2020-2023  润新知