C++中左值与右值

来源:互联网 发布:淘宝订单数据库表设计 编辑:程序博客网 时间:2024/05/16 18:56

让我们先看看一些常见的左值和右值举例:

int var = 6;          // var is a lvalue

const int var = 6;    // var is a (nonmodifiable) lvalue

char str[] = "hello, world"; // str is a (nonmodifiable) lvalue

"hello, world";       // the expression is string literal,

                       //so it's a rvalue

string("hello, world"); // the expresion is a (modifiable) rvalue


     对于每一个表达式,其要么是一个左值(lvalues),要么是一个右值(rvalue)。
   左值这个概念最初来源于赋值语句:E1 = E2;,这里要求左操作数E1是一个(可修改的)左值。现在,左值这个概念的含义已不局限于此。而右值是相对于左值出现的非左即右
   虽然左值和右值的内涵和外延有了很大变化,但仍满足这条规则:右值只能出现在赋值运算符的右边,不能出现在它的左边。
   一个左值对应于(refer to)一个对象或者函数(注:函数不是对象)[注1]

   右值的类型总是完整类型或者void类型。注意两点:(1)void类型(非完整类型)是右值;(2)当右值的类型不是void类型时,其必须是完整类型,因为需要计算它的值。由此可知,非void的非完整类型是左值

 

C++中的左值和右值

1.   概念
变量和文字常量都有存储区,并且有相关的类型,区别在于变量是可寻址的;
对于每个变量,都有2个值与其相关联:
1>数据值,存储在某个内存地址中,也称右值(rvalue),右值是被读取的值(read value),文字常量和变量都可被用于右值。
2>地址值,即存储数据值的那块内存地址,也称左值(lvalue),文字常量不能被用作左值。
2 . 问题
给表达式加上括号: ++a--
结果 ++(a--)
这个表达式是非法的,因为前增量操作要求一个可修改的左值,而 "a--" 不是左值(即右值)
3 . 前增量和后增量的区别
早期的c语言教材,for循环语句通常写成:
for(int i=0;i<10;i++)
而现在多为:
for(int i=0;i<10;++i)
两者有区别吗?
a++ 即是返回 a的值,然后变量 a 加 1,返回需要产生一个临时变量类似于
{
       int temp = a;
       a=a+1;
       return temp; //返回右值
}
++a 则为:
{
    a=a+1;
    return &a;    //返回左值
}
显然,前增量不需要中间变量,效率更高。

 

注1:对象类型为非函数、非引用、非void的类型;左值所对应的对象不一定实际存在。例如:char* p; *p这个左值所对应的对象目前是不存在的。

   部分右值表达式也可以对应对象。例如,那些调用构造函数和调用那些返回类对象的函数的表达式,这些表达式可以调用相应对象的成员函数,但这些表达式是右值(此时,该右值可能是可修改的右值)。

   如果在左值被计算时还没有指定一个对象,那么该计算行为是未定义的。例如:char* p;此时计算*p的结果是未知的。
   函数调用是左值,当且仅当返回类型是引用。[注2]
   在任何时候,当一个左值出现在需要右值的地方,左值会被转换成右值[注3]       

注3:当左值类型T为非函数、非数组类型时,左值可以被转换为右值。如果T是非void的非完整类型,那么此时左值不应该被转化为右值来使用;如果该左值所对应的对象不是类型T的对象,也不是由T派生的类型的对象,或者该对象没有被初始化,那么发生这种转换的程序将产生未定义行为。

   如果T是非类类型,那么转换得到的右值的类型是T的CV-qualified版本;否则,右值类型是T。

   左值所对应的对象所包含的值就是右值的结果。 在发生这种转换时,左值的值并不是一定被计算。例如:当运算符sizeof的操作数发生这种转换时,并不需要访问左值的值,因为该运算符不需要计算它的操作数。

   左值分为可修改的左值(modifiable)和不可修改的左值(nonmodifiable)。

   如果要修改一个对象,该对象必须是左值;例外情况是,类类型的右值也可以在某些情形下修改它所对应的对象。例如,函数调用返回的类是右值,但可以调用其成员函数修改该对象。

   由const限定的表达式不可以被修改;除非这是一个类类型,并且拥有mutable成员,那么该mutable成员可以被修改。

   非类类型的右值总是由CV-qualifiers限定,类类型的右值可以没有CV-qualifiers。

   部分内置运算符需要左值操作数。例如,所有内置赋值运算符要求它们的左操作数是一个左值(此时还是一个可修改的左值)。

  部分内置运算符需要右值操作数,并且产生右值。例如,一元运算符和二元运算符“+”要求操作数为右值,并且它们产生的结果也为右值。

   部分内置运算符和函数调用产生左值。例如,如果E是一个指针类型的表达式,那么*E是一个左值表达式(注:*运算符的操作数是左值,产生一个左值)。又如,函数 int& f();产生一个左值,因此f()的调用是一个左值表达式。

    如果一个程序试图通过一个左值的非下列类型去访问存储在对象中的值,那么该行为是未定义的。

注2:自定义运算符是函数,这类运算符是否需要或者产生左值取决于它们的参数和返回值类型。