22

谁能解释一下右值、左值、POD 和非 POD 的细节,为什么下面标记的第一个表达式正确而下面标记的第二个表达式可以?据我了解,int() 和 A() 都应该是右值,不是吗?


struct A {};

int main()
{
  int i;
  A a;

  int() = i; //Not OK (error).
  A() = a; //OK.

  return 0;
}

4

3 回答 3

19

右值是您从表达式中得到的(取自 C 标准的有用简化,但没有用 C++ 标准语言表述)。左值是“定位器值”。左值可以用作右值。引用总是左值,即使是 const。

您必须注意的主要区别可以浓缩为一项:您不能获取右值的地址(同样,不是标准的,而是对规则的有用概括)。或者换一种说法,你不能为右值确定一个精确的位置——如果可以的话,你就会有一个左值。(但是,您可以将 const& 绑定到右值以“将其固定到位”,而 0x 正在彻底改变规则。)

但是,用户定义类型 (UDT) 有点特殊:如果类的接口允许,您可以将任何右值转换为左值:

struct Special {
  Special& get_lvalue() { return *this; }
};
void f() {
  // remember "Special()" is an rvalue
  Special* p = &Special().get_lvalue(); // even though you can't dereference the
  // pointer (because the object is destroyed), you still just took the address
  // of a temporary

  // note that the get_lvalue() method doesn't need to operate on a const
  // object (though that would be fine too, if the return type matched)
}

除了通过编译器提供的赋值A() = a运算符将右值A()转换为*this. 引用标准,12.8/10:

如果类定义没有显式声明复制赋值运算符,则式声明一个。类 X 的隐式声明的复制赋值运算符将具有以下形式

X& X::operator=(const X&)

然后它继续具有更多的资格和规格,但这是重要的一点。由于这是一个成员函数,它可以在右值上调用,就像 Special::get_lvalue 一样,就好像你写A().operator=(a)的不是A() = a.

正如您所发现的那样,这int() = 1是明确禁止的,因为 int 没有 operator= 以相同的方式实现。但是,类型之间的这种细微差异在实践中并不重要(至少不是我发现的)。


POD 表示普通旧数据,是使用 memcpy 指定的要求的集合,相当于复制。非 POD 是您无法使用 memcpy 复制的任何类型(与 POD 天然相反,这里没有隐藏任何内容),这往往是您将用 C++ 编写的大多数类型。成为 POD 或非 POD 不会改变上述任何内容,并且确实是一个单独的问题。

于 2010-02-19T03:43:03.883 回答
2

据我了解,int() 和 A() 都应该是右值,不是吗?

正确,表达式T()始终是标量和用户定义类型的右值T。只要不const涉及,表达式T()就是一个可修改的右值,更准确地说。

涉及标量类型的赋值需要赋值运算符左侧的可修改左值。由于int()不是左值,因此您不能分配给int().

对于用户定义的类型,赋值是一种特殊的成员函数,成员函数也可以在右值上调用(参见 §3.10 第 10 节)。这就是为什么A().operator=(a)形成良好的原因。

于 2010-02-19T10:26:30.737 回答
1

来自C++ 是否对 POD typedef 进行值初始化?,其中引用了标准:

表达式 T(),其中 T 是非数组完整对象类型或(可能是 cv 限定的)void 类型的简单类型说明符 (7.1.5.2),创建指定类型的右值,即 value -初始化

因此 int() 是一个右值,不能分配给,正如您在第一个案例中看到的那样。

A() 不会是一个简单类型说明符,因此 A() 会产生一个左值

于 2010-02-19T03:26:25.480 回答