工作中遇到一个引用临时变量的问题,经过两天的学习,私以为:不仅弄明白了这个问题,还有些自己的独到见解。
这里使用一个简单的例子来把自己的学习过程和理解献给大家,如果有什么问题请不吝指正。
*************************Code*************************
class Dog
{
public:
Dog(){}
virtual ~Dog(){}
};
void NonConstReference (Dog & dog )
{
//tell the dog to do something here
}
void TestNonConstReference ()
{
NonConstReference( Dog());
}
*************************VS 2013, Level4 (/W4)*************************
warning C4239: nonstandard extension used : 'argument' : conversion from 'Dog' to 'Dog &'
*************************GCC, C++11*************************
-------------- Build: Debug in Test (compiler: GNU GCC Compiler)---------------
mingw32-g++.exe -Wall -fexceptions -g -std=c++11 -c G:\MyBackup\code\CodeBlock\Test\main.cpp -o obj\Debug\main.o
G:\MyBackup\code\CodeBlock\Test\main.cpp: In function 'void TestNonConstReference()':
G:\MyBackup\code\CodeBlock\Test\main.cpp:18:29: error: invalid initialization of non-const reference of type 'Dog&' from an rvalue of type 'Dog'
G:\MyBackup\code\CodeBlock\Test\main.cpp:11:6: error: in passing argument 1 of 'void NonConstReference(Dog&)'
Process terminated with status 1 (0 minute(s), 0 second(s))
2 error(s), 0 warning(s) (0 minute(s), 0 second(s))
*************************lvalue, xvalue, prvalue的一般定义*************************
首先lvalue, rvalue 都是针对表达式的;任何一个表达式都可以按照如下归类方式归类:
lvalue指代一个函数或者对象。例如:
- E是指针,则*E是lvalue
- 一个函数的返回值是左值引用,其返回值是lvalue。例如int& foo();
xvalue指代一个对象,但是和lvalue不同,这个对象即将消亡。
prvalue指代一个临时对象、一个临时对象的子对象或者一个没有分配给任何对象的值。例如:
- 一个函数的返回值是平常类型,其返回值是rvalue。例如int foo();
- 没有分配给任何对象的值。如5.3,true。
*************************lvalue, xvalue, prvalue的区分*************************
说明:这部分来自C++ PROGRAMMING LANGUAGE 4TH EDTION。
There are two properties that matter for an object when it comes to addressing, copying, and moving:
• Has identity: The program has the name of, pointer to, or reference to the object so that it is possible to determine if two objects are the same, whether the value of the object has changed, etc.
• Movable: The object may be moved from (i.e., we are allowed to move its value to another location and leave the object in a valid but unspecified state, rather than copying;).
It turns out that three of the four possible combinations of those two properties are needed to precisely describe the C++ language rules (we have no need for objects that do not have identity and
cannot be moved).
Using ‘‘m for movable’’ and ‘‘i for has identity,’’ we can represent this classification of expressions graphically:
.png)
So, a classical lvalue is something that has identity and cannot be moved (because we could examine it after a move), and
a classical rvalue is anything that we are allowed to move from.
*************************ISO IEC 14882 2011 8.5.3 References*************************
ISO文档使用cv来代表const volatile 修饰符。
并且假设我们使用这样的一种方式来赋值:cv1 T1 dest = cv2 T2 src;
举个例子就是:
int src = 123;
const int& dest = src;
void function(const int& dest){};
function(src);
ISO文档首先给出了两个概念:reference-related, reference-compatible。
Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is reference-related to “cv2 T2” if
- T1 is the same type as T2, or
- T1 is a base class of T2.
“cv1 T1” is reference-compatible with “cv2 T2” if
- T1 is reference-related to T2 and
- cv1 is the same cv-qualification as, or greater cv-qualification than, cv2.
说明:cv1 >= cv2的情况都有哪些呢:const > 没有修饰符, const volatile > const,etc.
分析一次赋值:cv1 T1 dest = cv2 T2 src; 是否合法采用如下4个步骤:
1.如果dest 是一个lvalue reference,同时:
1.1如果src是一个左值(不是一个bit-filed),并且cv1 T1 是 reference-compatible with cv2 T2的;
1.2如果T2是一个类类型(class, struct, union, etc.),即使cv1 T1 不是 reference-compatible with cv2 T2的,只要cv2 T2可以被转换成cv3 T3类型的一个左值(src1),这时如果cv1 T1 是 reference-compatible with cv3 T3的;
那么,dest 就帮定到src,或者src1上。
2.如果cv2 T2 src不能满足1.1,1.2,那么cv1 就应该是一个包含const的lvalue reference定义,否则它就因该是一个rvalue reference。此时如果cv2 T2满足如下条件:
2.1如果src是一个xvalue, 类类型的prvalue, array prvalue 或者返回左值的函数,并且cv1 T1 是 reference-compatible with cv2 T2的;
2.2如果cv2 T2是类类型的,即使cv1 T1 不是 reference-compatible with cv2 T2的,只要cv2 T2可以被转换成cv3 T3类型的一个2.1规定的值,假设是src1;
那么,dest就帮定到src,或者src1上。
3.如果cv2 T2 src也不能满足2.1,2.2,那么编译器就为src创建一个临时变量。
3.1创建此临时变量的条件是:cv1 T1 是 reference-related with cv2 T2,并且cv1 >= cv2;
4.如果cv2 T2 src不能满足上面所有的条件,那么cv1 T1就应该是一个rvalue reference。此时,如果cv2 T2是一个lvalue的话,编译器应该抱错。
*************************Reference 匹配(过滤)过程*************************
**************************************这里有些例子**************************************
-------------------------------能被规则1处理完毕-------------------------------------------------
double d = 2.0;
double& rd = d; //d, is an lvalue, and the cv1 equals cv2, 1.1能够处理
const double& rcd = d; // d, is an lvalue,
// the cv1 >= cv2: const > 没有修饰符,1.1能够处理
struct A { };
struct B : A
{
operator int&();
} b;
A& ra = b; // b, has a class type: struct;
//cv1 is reference related with cv2, ra is the base class of the b,1.2能够处理
const A& rca = b; // b, has a class type, struct;
//cv1 is reference related with cv2, ra is the base class of the b;
// the cv1 >= cv2: const > 没有修饰符,1.2能够处理
int& ir = B(); // B(), has a class type: struct
//it can be converted to an lvalue of the int&: operator int&()
//cv1 == cv2: cv修饰符都是空,1.2能够处理
------------------------------不符合规则1,被规则2处理-----------------------------------------
extern B f();
const A& rca2 = f();// f()返回值是一个类类型的rvalue,
// the cv1 >= cv2: const > 没有修饰符,2.1能够处理
struct X {
operator B();
operator int&();
} x;
const A& r = x;// x 是类类型的
// r 与x 不是reference-compatible的
// x 通过operator B()返回一个类类型的prvalue, tmpB
// r 与tmpB 的关系满足2.1的条件,2.2能够处理
-----------------------不符合规则1,也不符合规则2,被规则3处理---------------------------
const double& rcd2 = 2; // 2,不是一个lvalue/xvalue/类类型的prvalue/函数返回的左值,等。
// 创建一个临时变量2.0,3能够处理
--------不符合规则1,也不符合规则2,也不符合规则3,被规则4处理----------------------
double d2 = 1.0;
double&& rrd2 = d2; // rrd2是一个rvalue reference,不能使用lvalue 赋值。4能够处理
-----------------------------------------其他一些例子-------------------------------------------------------------
const volatile int cvi = 1;
const int& r2 = cvi; // error, in this example, the cv1 <= cv2, which violate the 1.1
*************************回到我们的例子*************************
class Dog
{
public:
Dog(){}
virtual ~Dog(){}
};
void NonConstReference (Dog & dog )
{
//tell the dog to do something here
}
void TestNonConstReference ()
{
NonConstReference( Dog());
}
NonConstReference( Dog())调用,在栈上创建了一个类类型的prvalue。
根据ISO文档,它不能规则1接纳,就只能由规则2继续处理。
规则2要求NonConstReference(Dog & dog )中的Dog & dog 必须是const Dog & dog。
而这里显然不是,所以抱错。
************************编译器为我们作了什么?语义分析*****************************
编译器,在严格的按照,c++语言的设计来执行语义检查:
- 目标是一个lvalue reference, 那么就不能给我一个rvalue.
- 要么就把目标设置成const lvalue reference.
如果一个参数是以非const引用传入,c++编译器就认为程序在函数中修改这个值,并且想要这个被修改过的值。
但如果你把一个临时变量当作非const引用参数传进来,程序并没有机会继续访问这样的变量,从而使修改一个临时变量变得毫无意义的。
从而c++编译器加入了临时变量不能作为非const引用的这个语义限制,意在限制这个非常规用法的潜在错误。
**************************************完*******************************************
- 重构手法之Introduce Explaining Variable(引用解释性变量)
返回总目录 6.5Introduce Explaining Variable(引用解释性变量) 概要 你有一个复杂的表达式. 将该复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表 ...
- 临时变量不能作为非const类型引用形参的实参
摘要: 非const 引用形参只能与完全同类型的非const对象关联. 具体含义为:(1)不能用const类型的对象传递给非const引用形参: ( ...
- 临时变量不能作为非const引用
转自:http://blog.csdn.net/u011068702/article/details/64443949 1.看代码 2.编译结果 3.分析和解决 就拿f(a + b)来说,a+b的值会 ...
- 非const引用不能指向临时变量
没找到具体原因,MSDN看到下面这句,VC是从2008才有这一限制的,感觉就是从语法上对临时变量增加了限定,因为一般说来修改一个临时变量是毫无意义的,通过增加限定,强调临时变量只读语义.虽然实际上修改 ...
- awk引用外部变量及调用系统命令方法
目标:想用awk与scp命令批量传送文件 前提:先搭好主机间的免密登陆环境(参考:http://www.cnblogs.com/tankaixiong/p/4172942.html) 实现脚本方法: ...
- [转] C++临时变量的生命周期
http://www.cnblogs.com/catch/p/3251937.html C++中的临时变量指的是那些由编译器根据需要在栈上产生的,没有名字的变量. 主要的用途主要有两类: 1) 函数的 ...
- c++ 临时变量
C++的临时变量 它们是被神所遗弃的孩子,没有人见过它们,更没有人知道它们的名字.它们命中注定徘徊于命运边缘高耸的悬崖和幽深的深渊之间,用自己短暂的生命抚平了生与死之间的缝隙.譬如朝露,却与阳光无缘. ...
- C++临时变量的生命周期
C++ 中的临时变量指的是那些由编译器根据需要在栈上产生的,没有名字的变量.主要的用途主要有两类: 1) 函数的返回值, 如: string proc() { return string(" ...
- Mysql 临时变量的 定义 和 赋值 Set 和 Into 赋值; Swith Mysql版本 Case When的用法
一:临时变量的定义和赋值 DECLARE spot SMALLINT; -- 分隔符的位置 DECLARE tempId VARCHAR(64); -- 循环 需要用到的临时的Cid DECLARE ...
随机推荐
- 对C语言的知识与能力予以自评
看到一个问卷不错,拟作为第三次作业的部分内容. 你对自己的未来有什么规划?做了哪些准备?我准备在将来成为一名合格的软件工作人员,我已经在平时有空的时间里着手代码的练习. 你认为什么是学习?学习有什么用 ...
- android aidl 进程间通信需要注意的地方(android.os.TransactionTooLargeException)
转自:http://blog.sina.com.cn/s/blog_4e1e357d0102wau9.html 1.bus工程实现通过service实现aidl实体类 2.actor工程通过发起bin ...
- jsonp解决跨域
ajax请求: $.ajax({ type: "get",//必须使用get方式 async: false, url: "htt ...
- Hello Docker (Docker 入门分享)
Hello Docker 1 PPT @ http://download.csdn.net/download/liangread/9431056 TBD
- 表单和 HTML 辅助方法– ASP.NET MVC 4 系列
这里有一个疑问,诸如在文本编辑器中输入 HTML 元素如此简单的任务,也需要任何帮助吗?的确,输入标签名称是很容易的事,但是确保 HTML 页面链接中的 URL 指向正确的位置.表单元素 ...
- Centos 6.5编译安装yum
1.删除redhat原有的yum rpm -aq|grep yum|xargs rpm -e --nodeps 2.下载yum安装文件 wget http://mirrors.163.com/cent ...
- iTunes安装app总是提示授权失败
今天打算使用iTunes安装app,手机和电脑都确认授权了,始终提示如下信息:
- 关于FPGA学习路线
1.参考FPGA厂商的参考资料,将某系列FPGA所有芯片资料下载下来,有针对性的做参考. 2.参考FPGA厂商开发板以及相应的参考设计,在开发板里有众多的外围接口电路,基本涵盖了常用的应用场合.同时也 ...
- mvc中Url.RouteUrl或者Html.RouteLink实现灵活超链接,使href的值随路由名称或配置的改变而改变[bubuko.com]
mvc,超链接除了直接写在a标签的href内还可以使用路由规则来生成,这样在改变了路由规则或者路由名称时不用再去代码中更改href的值,而且还容易遗漏.借助Url.RouteUrl或者Html.Rou ...
- Python多线程、进程入门1
进程是资源的一个集合, 1.一个应用程序,可以有多进程和多线程 2.默认一个程序是单进程单线程 IO操作使用多线程提高并发 计算操作使用多进程提高并发 进程与线程区别 1.线程共享内存空间,进程的内存 ...