10

当我阅读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(); 不是错误。后一个是左值,而第一个是右值?

提前致谢

4

2 回答 2

13

假设我们有下面显示的 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 是一个右值,它不能用作“++”的参数。反过来,这意味着上面的代码将无法编译。

于 2013-06-28T05:42:32.017 回答
8

所以我们有两个功能:

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

函数调用表达式:

foobar()
foo()

两者都有 int 类型(引用从表达式中删除,所以foo()有 typeint和 not lvalue-reference to int)。这两个表达式具有不同的值类别:

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

您不能获取右值的地址(右值是一种右值),因此&foobar()是不允许的。

您可以获取左值的地址,因此&foo()是允许的。

于 2013-06-28T05:42:03.283 回答