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

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

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

常量右值引用可以绑定到非常量右值和常量右值,不能绑定到非常量左值和常量左值(理由同上)。可以看出,使用左值引用时,我们无法区分出绑定的是否是非常量

时间: 2024-10-27 03:46:53

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

c++产生非常量引用的初始值必须是左值

出现这种情况是因为调用函数的形参类型是  T&(T为某一类型),该类型与调用参数值传入不一致,比如函数中形参是int型,而传入实参是double,存在隐式类型转化,系统会自动产生临时变量temp将常量进行了修改. 解决的办法,1避免隐式调用,如:int y=5:double x=(double)y,然后再将x值传入函数 2.在形参面前加const,避免了修改形参. 原文地址:https://www.cnblogs.com/sozze011/p/11257281.html

对const的引用(常量引用)

引用的类型必须与其所引用对象的类型一致.但是,对const的引用是第一种例外情况. 对const的引用,常简称为"常量引用". 常量引用的初始值可以是任意表达式,只要该表达式的结果能转换成引用的类型即可. 但是,对于字面值,一般表达式以及与常量引用类型不一致的对象作为常量引用的初始值,常量引用实际绑定到了临时变量上.测试代码如下: 1 #include <iostream> 2 3 int main() 4 { 5 int i = 42; 6 int &r1 = i

【转载】C++引用详解

原文:http://www.cnblogs.com/gw811/archive/2012/10/20/2732687.html 引用的概念 引用:就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样. 引用的声明方法:类型标识符 &引用名=目标变量名: 如下:定义引用ra,它是变量a的引用,即别名. int a; int &ra=a; (1)&在此不是求地址运算符,而是起标识作用. (2)类型标识符是指目标变量的类型. (3)声明引用时,必须同时对其进行初始化. (

i++与++i作为左值

#include <iostream> using namespace std; int main() { int i = 9; //i++ = 10; ++i = 10; return 0; } /* i++ 不可以作为左值: ++i 可以作为左值. C++primer 中关于左值右值的说法: 变量和文字常量都有存储区,并且有相关的类型.区别在于变量是可寻址的(addressable)对于每一个变量都有两个值与其相联: 1).它的数据值,存储在某个内存地址中.有时这个值也被称为对象的右值(r

C++标准的规定:非常量的引用不能指向临时对象(转载)

C++标准的规定:非常量的引用不能指向临时对象: 为了防止给常量或临时变量(只有瞬间的生命周期)赋值(易产生bug),只许使用const引用之. 下面的内容转自: http://blog.csdn.net/liuxialong/article/details/6539717 概括一下: 不能把临时对象作为实参传给非const引用. 例如: void conv(string &str) { } int main() { conv("dasd"); // 这里错了,编译器自动生成一

【转】int &amp;&amp; 非常量右值

C++ 11中引入的右值引用正好可用于标识一个非常量右值.C++ 11中用&表示左值引用,用&&表示右值引用,如: int &&a = 10 右值引用根据其修饰符的不同,也可以分为非常量右值引用和常量右值引用. C++ 11中引入的一个非常重要的概念就是右值引用.理解右值引用是学习“移动语义”(move semantics)的基础.而要理解右值引用,就必须先区分左值与右值. 对左值和右值的一个最常见的误解是:等号左边的就是左值,等号右边的就是右值.左值和右值都是针对

CSAPP读书随笔之一:为什么汇编器会将call指令中的引用的初始值设置为-4

CSAPP,即<深入理解计算机系统:程序员视角>第三版,是一本好书,但读起来确需要具备相当的基本功.而且,有的表述(中译文)还不太直白. 比如,第463页提到,(对于32位系统)为什么汇编器会将call指令中的引用的初始值设置为-4.其后解释语焉不详.结合文中对代码计算公式的展开: *refptr = (unsigned) (ADDR(r.symbol) + *refptr - refaddr) = (unsigned) (0x80483c8        + (-4)     - 0x804

C++11之右值引用:从左值右值到右值引用

C++98中规定了左值和右值的概念,但是一般程序员不需要理解的过于深入,因为对于C++98,左值和右值的划分一般用处不大,但是到了C++11,它的重要性开始显现出来. C++98标准明确规定: 左值是可以取得内存地址的变量. 非左值即为右值. 从这里可以看出,可以执行&取地址的就是左值,其他的就是右值. 这里需要明确一点,能否被赋值不是区分C++左值和右值的区别. 我们给出四个表达式: string one("one"); const string two("two&

引用、左值与右值

我的理解 引用是一个左值,而常量引用是一个右值.两者最关键的地方在于,左值可以被取到地址,而右值取不到地址,这个性质就决定了右值不能在 "=" 的左侧. 从汇编角度去理解这个问题的话,就很好理解了,左值是一个内存单元里的数据,我们可以直接寻址找到它,而右值是一个立即数或者是寄存器里的值. C与C++中的左值.右值 C 中可以通过赋值号 "=" 简单地判断,比如 int a = 42; // 表达式a是左值,字面值常量42是右值 int b = 43; // 表达式b