【问题标题】:PODs, non-PODs, rvalue and lvaluesPOD、非 POD、右值和左值
【发布时间】:2011-01-18 14:59:27
【问题描述】:

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


struct A {};

int main()
{
  int i;
  A a;

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

  return 0;
}

【问题讨论】:

    标签: c++ rvalue lvalue


    【解决方案1】:

    来自引用标准的Does C++ do value initialization of a POD typedef?

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

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

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

    【讨论】:

    • 链接循环到这个页面。 §5.2.3 明确规定 T() 是一个右值,无论类型是什么。
    • 修复了FireFox中的链接错误(切换标签时没有更新地址栏)
    • 即使链接错误,答案也是正确的,因为链接指向的页面带有指向页面的链接,带有指向页面的链接...带有引号。太糟糕了,它不是尾递归。 :-)。
    • A 一个简单的类型说明符。
    • A() 是一个右值,而不是一个左值。
    【解决方案2】:

    右值是你从表达式中得到的(从 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 并不会改变上述任何一项,并且确实是一个单独的问题。

    【讨论】:

    • C 特定,但对 C++ 程序员理解仍然有用:stackoverflow.com/questions/2038414/lvalue-and-rvalue/…。 (这是两种语言从根本上共享的一个领域,尽管 C++ 的引用要复杂得多,并且实际上使用了 C 不再使用的术语“右值”。)
    • "或者换一种说法,你不能为右值确定一个精确的位置" 根据语言标准,基本类型的右值甚至不是一个对象,没有地址。
    【解决方案3】:

    在我的理解中,int() 和 A() 都应该是右值,不是吗?

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

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

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

    【讨论】:

    • int() 是一个可修改的右值
    • @curiousguy:是的。 There are no const scalar rvalues.
    • @curiousguy:别担心,没有办法修改可修改的标量右值。不要让名称误导您;)“可修改”只是“非常量”的标准术语,并且由于标量右值永远不是 const,因此它们是可修改的,即使您无法修改它们。
    • 好吧,IMO 这有点恶毒。
    • @curiousguy:返回int 的函数和返回const int 的函数有什么区别?人们认为const 没有在此处添加任何内容,因此它被简单地忽略了。
    猜你喜欢
    • 2011-04-25
    • 2021-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多