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

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

转载于:https://www.cnblogs.com/zhoug2020/p/7821125.html

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

  1. 非常量引用的初始值必须为左值解决方式

    看下面的这个函数与调用,这个时候就会出现非常量引用的初始值必须为左值解决方式的错误 修改方法:函数声明的时候,加一个constPoint3f getWorldPoints(const Point2f& ...

  2. C++中“非常量引用的初始值必须是左值”的处理方法

    原文:https://blog.csdn.net/hou09tian/article/details/80565343 1 左值和右值 在C++中,左值可以出现在赋值语句的左边和右边:右值只能出现在赋 ...

  3. 非常量引用的初始值必须是左值_C++核心编程--引用

    2 引用 2.1 引用的基本使用 作用: 给变量起别名 语法: 数据类型 &别名 = 原名 示例: int main() {int a = 10;int &b = a;cout < ...

  4. 非常量引用的初始值必须为左值

    问题描述 先看一段报错的代码: #include<iostream> using namespace std;int main() {int i = 2;double &r = i ...

  5. ×××××sales_data通过类型转换形成的临时对象不能转换成sales_data (非常量引用)

    文件   sales_data.h #include <iostream> #include <string> using namespace std; struct sale ...

  6. 临时变量不能作为非const引用

    int func(a) {return a; }int main() {int r = func(3 + 5);return 0; } 上述代码报错, 非常量引用的初始值必须为左值 .  主要原因如下 ...

  7. c++左值和右值、 左值引用与右值引用

    c++ 左值引用与右值引用--原 左值和右值 综述 C++对于左值和右值没有标准定义,但是有一个被广泛认同的说法: 可以取地址的,有名字的,非临时的就是左值:--变量.返回引用.const 不能取地址 ...

  8. 左值右值,左值引用和右值引用及其用途

    目录 1.左值和右值 2.引用 (1)左值引用 (2)右值引用 3.左值引用的用途 (1)作为复杂名称变量的别名 (2)用于rangeFor循环 (3)避免复制大的对象 (4)参与函数中的参数传递 4 ...

  9. C++ 常量引用用法详解

    "常量引用"其实是"对 const 的引用"的简称. 顾名思义,它把它所指向的对象看作是常量(不一定是常量),因此不可以通过该引用来修改它所指向的对象的值. 严 ...

最新文章

  1. Jenkins安装maven integration plugin失败解决方法
  2. 如何使错误日志更加方便排查问题
  3. fftw库 vs2019_FFTW库在VS 2010中的使用方法
  4. socket的NIO操作
  5. iOS 5将加入全新的通知信息和桌面Widgets
  6. [react] react兄弟组件如何通信?
  7. python假如输入错误重新输入_认识python之输入(4)
  8. usage: git remote add [options] name url -f, --fetch fetch the remote branches ...
  9. 使用Asp.net MVC 2.0 +.NET 4.0 出现 “从客户端 ... 中检测到有潜在危险的 Request.Form 值”错误的解决办法...
  10. 学习笔记——RuntimeException
  11. [挖坟] 突破WINISO未注册时100M限制
  12. 算术关系和逻辑关系---皮尔斯逻辑之二
  13. Google桌面搜索使用与技巧
  14. postman安装使用教程(标贝科技)
  15. keil5工程函数无法跳转到函数定义解决方法
  16. 基于Wemos的wifi避障小车部分代码
  17. 使用阿里云服务器三分钟搭建网站
  18. Windows下x64反汇编参数传递约定,一句话,调用顺序为从左到右, Function( rcx, rdx, r8,r9, [rsp+0x20], [rsp+0x28], [rsp+0x30]..
  19. 调用百度汇率api 获取各国的汇率值
  20. Linux基本命令 初级10个

热门文章

  1. python编程中常用的12种基础知识总结
  2. imp导入时出现imp-00017 ora-06550的解决办法
  3. Win7 一键获得管理所有权限(最高权限)注册表
  4. 网络学习(二十七)Windows XP 加入 Windows Server 2003 Active Directory
  5. SQL Server 2008带字段注释导入Power Designer 9.5
  6. 如何做一份出色的竞品分析?(一)
  7. NPV Formula in Excel
  8. 关于mbzuai的offer的三点思考
  9. C# StopWatch的BUG????
  10. 字符串周期--hdu 3746 Cyclic Nacklace