rvalue和lvalue的确切区别是什么?

12

我在阅读http://thbecker.net/articles/rvalue_references/section_01.html时,发现以下代码片段。

// lvalues:
//
int i = 42;
i = 43; // ok, i is an lvalue
int& foo();
foo() = 42; // ok, foo() is an lvalue
int* p1 = &foo(); // ok, foo() is an lvalue

// rvalues:
//
int foobar();
int j = 0;
j = foobar(); // ok, foobar() is an rvalue
int* p2 = &foobar(); // error, cannot take the address of an rvalue
j = 42; // ok, 42 is an rvalue

为什么语句int* p2 = &foobar()是错误的,而int* p1 = &foo()不是错误的。如何解释第二个语句是左值而第一个语句是右值?


我相信你最后一句话的顺序是颠倒的。 - Karthik T
2个回答

14

假设我们有下面这段C代码的示例。它能编译吗?在这个问题中,左值和右值的概念是如何工作的呢?

#define X 8
int main(void)
{
    ++X; // will this line compile?
        return 0;

}

为了真正理解上面的代码和问题,必须先解释一下左值和右值的概念。在继续之前,您应该注意这里介绍的左值和右值的定义并不精确,因为即使是C标准本身也对其定义相当含糊。

左值和右值的区别

对象是可被查看但不一定可被修改的内存区域。左值是指引用这种对象的表达式。左值这个词最初是指出现在表达式左侧(因此是“l”的)的对象。由于任何const限定类型也被认为是左值,因此该定义已不再适用,但它永远不可能出现在赋值语句的左侧,因为它不能被修改。因此,“可修改的左值”这个术语被创造出来,用来指代可以被修改的左值,而const限定类型不属于这个类别。

右值是任何具有值但不能被分配值的表达式。也可以说右值是任何不是左值的表达式。右值的一个示例是字面常量,比如 "8" 或 "3.14"。所以,显然代码中的值 "8" 是一个右值。

使用我们对左值和右值的理解来回答问题

现在让我们尝试解决这个问题。严格来说,前缀(或后缀)递增运算符的操作数必须是可修改的左值。那么在我们上面的代码中,前缀递增运算符的操作数是什么?

由于 X 是一个宏,所以在预处理器运行之后,上面的语句将扩展为“++8”。这意味着“8”是前缀递增运算符的操作数。而因为 8 是一个右值,所以它不能作为 "++" 的参数使用。这反过来又意味着上面的代码将无法编译。


2
非常好,但为什么要费心去做这个例子呢?我不明白这种小测验问题如何帮助理解这个问题。 - SChepurin

10

所以我们有两个函数:

int& foo();
int foobar();
  • foo是一个返回int左值引用的函数
  • foobar是一个返回int的函数

函数调用表达式:

foobar()
foo()

两者类型都是整型(表达式中去除引用,所以foo()的类型是int而不是左值引用到int)。 这两个表达式有不同的值类别:

  • foobar()是prvalue(对返回非引用函数的函数调用是prvalue)
  • foo()是lvalue(对返回左值引用的函数调用是lvalue)

无法取rvalue地址(prvalue是rvalue的一种),因此不允许使用&foobar()

可以取lvalue地址,所以&foo()是允许的。


为什么无法获取foo的地址? - Pranit Kothari
@ss7don:你是指表达式 foo() 吗?你可以取得 foo() 的地址。请再次阅读我的回答的最后一行。 - Andrew Tomazos
我已经接受了这个答案,因为它看起来很合理。坦白地说,我仍然不明白foo和foobar之间的确切区别。它们都是函数。但是一个是右值,另一个是左值,这是怎么回事呢? - Pranit Kothari
4
价值类别是表达式的属性,而不是函数的属性。函数调用表达式的价值类别是基于其返回类型计算的。函数foo的返回类型是int&。函数foobar的返回类型仅为int。函数调用的价值类别取决于该函数调用是否带有引用符号& - Andrew Tomazos
谢谢。现在我明白了值和函数之间的区别,消除了我的疑惑。 - Pranit Kothari
为什么 foobar() 不是 xvalue?因为将要返回的值会在函数调用后过期。 - Majid Azimi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接