为什么(a = b)= c C++中的合法语法?

7bi*_*sso 31 c++ operators

如果一个语句(a = b) = c;在C++中工作,假设是什么a,b或者cints或任何其他原始类型?

Ker*_* SB 57

赋值表达式a = b不是C中的左值,但它在C++中:

  • C11,6.5.14(分配操作员):

    赋值运算符将值存储在左操作数指定的对象中.赋值表达式在赋值后具有左操作数的值,但不是左值.

  • C++ 14,5.18 [expr.ass](赋值和复合赋值运算符):

    赋值运算符(=)和复合赋值运算符都是从右到左分组.所有都需要一个可修改的左值作为左操作数,并返回一个左值操作数的左值.

在C语言从C语言的演变中,有几个表达式被称为"左值感知",因为它在C++中比在C中更重要.在C语言中,一切都是微不足道的(通常是可复制的和平凡的可破坏的,所有这些都在C++的话,所以左值到右值的转换(或"左值转换",如C调用它们)并不痛苦.在C++中,复制和破坏是非平凡的概念,通过使表达式保持左值,可以避免许多复制和破坏,这是从来没有必要开始的.

另一个例子是条件表达式(a ? b : c),它不是C中的左值,但可以是C++中的左值.

这种语言演变的另一个有趣的假象是C有四个明确定义的存储持续时间(自动,静态,线程局部,动态),但在C++中这变得更加混乱,因为临时对象在C++中几乎是一个非平凡的概念要求自己的存储时间.(例如Clang内部有第五个"完整表达"存储持续时间.)临时数据当然是左值到右值转换的结果,所以通过避免转换,可以少担心一件事.

(请注意,所有这些讨论仅适用于相应的核心语言表达式.C++还具有独立的,无关的运算符重载特性,它产生函数调用表达式,它具有函数调用的所有常用语义,与之无关.除了语法之外的运算符.例如,您可以定义一个operator=返回prvalue 的重载或者void如果您愿意的话.)

  • `(a?b:c)= d;`那么意味着取决于`a`的值,`d`将被分配给`b`或`c`? (8认同)
  • @KerrekSB更糟糕的是,`(c?v1:v2).clear()`编译得很好(因为我们可以在temporaries上调用非`constst`函数),但是默默地什么都不做._*不寒而栗*_ (8认同)
  • @PaulOgilvie:这是[正确](https://ideone.com/CiJZzH).你甚至可以做像`(c?v1:v2).clear()`这样的事情.如果条件运算符总是导致prvalue,那么您将永远无法影响操作数值. (7认同)
  • @PaulOgilvie该函数将在一个临时函数上调用,该函数可以从`v1`或`v2`复制构造,保持两个原始对象不变. (4认同)
  • 我甚至可以说左值引用是一个设计用于允许*用户定义的左值表达式的工具.同样,rvlalue引用是一个工具,可以启用任意的右值到左值的转换(在C中不存在,只有作为作者的工具才能通过C++中的类成员函数存在.值类别是基本概念;引用是使用值类别的工具. (4认同)
  • @Quentin,啊.....太可怕了! (2认同)

Pet*_*ker 22

非正式地,在C++中,对于内置类型,结果a = b是引用a; 您可以为该引用分配值,就像使用任何其他引用一样.因此(a = b) = c赋值bto a,然后赋值cto a.

对于用户定义的类型,这可能不适用,尽管通常的习惯用法是赋值操作符返回对左侧参数的引用,因此用户定义类型的行为模仿内置类型的行为:

struct S {
    S& operator=(const S& rhs) {
        return *this;
    }
};
Run Code Online (Sandbox Code Playgroud)

现在,S a, b, c; (a = b) = c;意味着调用a.operator=(b),它返回一个引用a; 然后调用S::operator=该结果,并c有效地调用a.operator=(c).

  • 表达式的结果永远不会引用."a = b`*的结果是*值"a"(但是已经发生了改变"a"的副作用). (9认同)
  • 是的,是的,我很欣赏......但在向人们解释二十次并计算右值参考不是rvalues之后,我开始尝试从一开始就教导诚实的信息,希望永远不会产生误解.第一名.这不是对你的回答的批评,我只想把这个细节记录在某处:-) (8认同)
  • @KerrekSB - 感叹.这就是我说"非正式"的原因.请不要把它拉到一个rathole. (5认同)
  • @Olaf - 答案不是关于C. (2认同)
  • @PeteBecker:这是我提到的问题. (2认同)