C++:神一样的左值

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++:神一样的左值相关的知识,希望对你有一定的参考价值。

const int ii = 10 ;
&static_cast< unsigned int>( ii ) ; // error, 取地址需要左值
& ii ; // ok, ii 是左值,可以取地址
++ ii // error, ii 不是左值,不能++

左值:
1. 命名对象都是左值,临时对象不是左值
2. const对象有时候是左值,有时候不是.

C++满大街的使用"左值"这个名词
我的问题1: 什么是左值?

问题2:
int func( wchar_t* )



unsigned int c ;
func( &static_cast< wchar_t >( c ) ) // error,需要左值

func( static_cast< wchar_t*>( &c ) ) // error, unsigned int* 不能转到 wchar_t*

问题:不使用reinterpret_cast<>,只使用一句表达式怎么让func调用成功.

PS:这不是什么试题,是我现实遇到的麻烦.

关于左值:
首先你要知道,计算机是数学的一个分支,所以一开始作为形式语言发展而来的一个惯用法就是左值与右值的语法区分,也被当今的大多数语言所继承。
关于这一点请百度搜索一片csdn上的文章:《C/C++左值性精髓》。
当然有些语言对左右值的区分逐渐淡化,或者你可以认为是变得更加复杂。一下是c++11关于左右值的解释:
C++11中对LValue和RValue的界定更加详细而合理(但是也更加麻烦了)
1章节:C++11的新分类介绍
2章节:不同值之间的转换
3章节:对一些常见的表达式,如何区分他们是什么值?
1. 表达式分类
所谓Value,是对表达式而言的。一个表达式可以分为以下几种Value,下边详细说明
1.1. lvalue(左值)
lvalue指代一个函数或者对象。例如:
E是指针,则*E是lvalue
一个函数的返回值是左值引用,其返回值是lvalue。例如int& foo();
1.2. xvalue(expiring value,临终值)
xvalue指代一个对象,但是和lvalue不同,这个对象即将消亡。具体来说,xvalue是包含了右值引用的表达式。因为右值引用是C++11新引入的东西,所以xvalue也是一个新玩意。例如:
一个函数的返回值是右值引用,其返回值是xvalue。例如int&& foo();
1.3. glvalue(generalized lvalue,泛左值)
glvalue即lvalue和xvalue的统称。
1.4. rvalue(右值)
rvalue是xvalue和prvalue的统称。因为引入了右值引用,rvalue的定义在C++中被扩大化了。
1.5. prvalue(pure rvalue,纯右值)
prvalue指代一个临时对象、一个临时对象的子对象或者一个没有分配给任何对象的值。prvalue即老标准中的rvalue。例如:
一个函数的返回值是平常类型,其返回值是rvalue。例如int foo();
没有分配给任何对象的值。如5.3,true。
2. 表达式值类型的转换
glvalue → prvalue
其实表达式的转换,只有以上一途,也就是
lvalue → prvalue
xvalue → prvalue
2.1. lvalue → prvalue
这个很常见:glvalue(lvalue和xvalue)可以隐式转换为prvalue来满足需求。例如:
int& foo(int val) // 函数接受prvalue
int a = val + 1; return a;
int main()
int i = 5;
cout << foo(4) << endl; // 传递进去一个prvalue
cout << foo(i) << endl; // 传递进去一个lvalue
cout << foo(foo(i)) << endl; // foo(i)返回lvalue,传递至外层函数
system("pause");

当然,这个函数写的不大好,不应该将临时对象传递出函数的~
如果将foo改写为下面这样,上边的代码就不能工作了,因为prvalue→lvalue是不成滴~
?1234int& foo(int val) return val + 1;
2.2. cv-qualifier的解除
在其他转换中,const转换是不能去除的,但是glvalue到prvalue的转换是可以去除的。这个说辞比较晦涩,其实十分常见,比如:
?const int a = 5;int b = a; // b = ? 这个需要右值,所以把a先转为右值,同时丢弃了const限定词
3. 表达式值类型的判定
C++的每一个表达式都有其值类型,换句话说,任何表达式,都属于以下三者之一:lvalue,xvalue,prvalue。
如果是简单的表达式自然很好理解,如上一节所举的变量、常量以及返回值。但是大部分情况都要复杂得多。
2.1. C++内建操作的判定
这个就要具体操作具体分析了。C++的内建操作对操作数的类型做出了详细的界定,对整个操作代表的类型也做出了详细的界定。例如:
分配运算 op1 = op2。op1是lvalue,op2是rvalue。整个操作是lvalue
2.2. 用户定义的操作
用户定义操作,无非就是自建函数。(恩,没错,C++的操作符重载也是函数重载~~)操作数等价于函数参数,操作yield值相当于函数返回值。其类型都由用户界定。例如:
int foo(int); 参数是prvalue,返回值是prvalue。
int& foo(int); 参数是prvalue,返回值是lvalue。u

关于第二个问题的话:
首先我需要先警告你,如果你现实中真遇到这种问题的话,你的设计和实现绝对是存在问题的,而且可能很严重。暂且忽略这个……我们来说说强制类型转换的问题。
熟悉C++的类型转换之前,首先要知道C中有两种类型转换模式:强制转换和隐式转换。
C++在设计之初就决定尽量避免隐式类型转换,从后来的标准更新中也可以看出来,为了代替隐式类型转换提出了使用static_cast关键字进行的转换,static_cast在最初用来代替隐式类型转换以消除掉任何不安全的隐式转换,所以static_cast让某些编译器会警告的转换直接通过,所以是一种又用户保证安全性的转换。
reinterpret_cast<> 简单地说就是C里的强制类型转换!

所以func( )这货和左值不左值的没任何关系。
解决方法如下:
func( static_cast< wchar_t*>(static_cast<void*>( &c )) );

或者func( (wchar_t*)( &c ) )
参考技术A 最原始也是最直观的解释方法就是:赋值运算符左边的叫做左值,右边的叫做右值……
至于是否所有左值都是可修改的,是否所有可修改的都是左值,C++最新标准的回答是:都不是追问

所以希望一个C++里,可以用以依赖的左值说法.

追答

左值在同一个作用域中,在内存中(或寄存器中)的位置是不变的,一定是具名的或通过其他具名对象间接得到的(例如a,或*p),而右值则不是……这个解释如何?
另外C++11是有lvalue、xvalue、prvalue三种的,具体可看标准原文……

本回答被提问者采纳
参考技术B 在赋值号左边的 变量 叫左值
左值有一个对应的内存地址
比如 int i; i就是左值
相对应的 右值是确定的数据
如i=10; 把10赋给i
简单来说就是,左值相当于地址值,右值相当于数据值

以上是关于C++:神一样的左值的主要内容,如果未能解决你的问题,请参考以下文章

c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward)

左值左值引用右值右值引用

C++11:左值右值左值引用右值引用

C++11:左值右值左值引用右值引用

C++的左值 右值 左值引用 右值引用 大白话总结

C++11:左值右值左值引用右值引用有什么区别?