假设(a = b) = c;
,a
和b
是c
s或其他任何原始类型,int
这样的语句如何在C ++中工作?
答案 0 :(得分:56)
赋值表达式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内部有第五个,"full expression" storage duration。)临时值当然是左值到右值转换的结果,所以通过避免转换,可以少担心一件事。
(请注意,所有这些讨论仅适用于相应的核心语言表达式.C ++还具有运算符重载的独立,无关的特性,它产生函数调用表达式,它具有所有通常的函数调用的语义与除语法之外的运算符无关。例如,您可以定义一个重载operator=
,如果您愿意,可返回prvalue或void
。)
答案 1 :(得分:21)
非正式地,在C ++中,对于内置类型,a = b
的结果是对a
的引用;您可以为该引用分配值,就像使用任何其他引用一样。因此,(a = b) = c
会将b
的值分配给a
,然后将c
的值分配给a
。
对于用户定义的类型,这可能不适用,尽管通常的习惯用法是赋值操作符返回对左侧参数的引用,因此用户定义类型的行为模仿内置类型的行为:
struct S {
S& operator=(const S& rhs) {
return *this;
}
};
现在,S a, b, c; (a = b) = c;
表示调用a.operator=(b)
,它会返回对a
的引用;然后在该结果和S::operator=
上调用c
,有效地调用a.operator=(c)
。
答案 2 :(得分:3)
vm.DTColumnDefs = [
DTColumnDefBuilder
.newColumnDef(-1)
.withOption('createdCell', function (td, cellData, rowData, row, col) {
$(td).attr('data-title', cellData)
})
]
是C ++中的有效语句。这里'='作为赋值运算符。此处, b 的值将分配给 a , c 的值将分配给 a 从右到左优先。
例如:
(a = b) = c
输出:
int a = 5;
int b = 2;
int c = 7;
int answer = (a = b) = c;
cout << answer << endl;
答案 3 :(得分:1)
以下是一点点推测,如果我错了,请纠正我。
当他们发明运算符重载时,他们必须为任何类T
提出一个标准外观的赋值运算符。例如:
T& T::operator=(T);
T& T::operator=(const T&);
此处,它会返回对T
的引用,而不仅仅是T
,以使x = (y = z)
这样的三部分分配更有效,而不需要副本。
它可能会返回对const
的{{1}}引用,这会使不需要的分配T
出错。我猜测他们没有使用它,原因有两个:
(a = b) = c
(const
的详细信息 - 当时不正确)const
,其中(a = b).print()
是非print
方法(因为程序员懒惰/不了解const
- 正确性)原始类型(不是const
es)的语义是外推的,给出:
class
&#34;返回类型&#34;不是int& operator=(int&, int); // not real code; just a concept
因此它与const int&
es匹配。因此,如果错误的class
代码对用户定义的类型有效,那么它对于内置类型也应该是有效的,如C++ design principles所要求的那样。一旦你记录了这种东西,你就不能因为向后兼容而改变它。