关于赋值语句的 C++ 语法实际上有两件有趣的事情,它们都与以下内容的有效性无关:
(4 || localvar1) = 5;
由于括号,该表达式在语法上是有效的(直到类型检查)。 任何引用类型的括号表达式在赋值运算符的左侧在语法上是正确的。 (而且,正如已经指出的那样,几乎任何涉及用户类型或函数的表达式都可以是引用类型,这是运算符重载的结果。)
该语法更有趣的是,它建立了赋值运算符的左优先级低于几乎所有其他运算符,包括逻辑或,因此上述表达式在语义上等价于
4 || localvar1 = 5;
尽管许多读者会将上述内容解释为4 || (localvar1 = 5)(假设localvar1 是可以由int 分配的类型,这将是完全正确的,即使所述分配永远不会发生 - - 当然,除非 || 在这种情况下被重载)。
那么,赋值运算符左侧的优先级较低的是什么?正如我所说,很少,但一个重要的例外是?::
// Replace the larger of a and b with c
a > b ? a = c : b = c;
是有效且方便的无括号。 (许多风格指南在这里坚持使用多余的括号,但我个人更喜欢不带括号的版本。)这与右手优先不同,因此以下也适用于不带括号的情况:
// Replace c with the larger of a and b
c = a > b ? a : b;
在赋值运算符左侧绑定比赋值运算符更松的其他运算符是, 运算符和另一个赋值运算符。 (换句话说,与几乎所有其他二元运算符不同,赋值是右关联。)这些都不足为奇——事实上,它们是如此必要,以至于很容易忽略设计的重要性这样的语法。考虑以下不起眼的for 子句:
for (first = p = vec.begin(), last = vec.end(); p < last; ++p)
这里的, 是一个逗号操作符,它显然需要比它周围的任何一个赋值更紧密地绑定。 (C 和 C++ 仅在此语法中例外,因为它具有逗号运算符;在大多数语言中,, 不被视为运算符。)此外,显然不希望将第一个赋值表达式解析为 (first = p) = vec.begin()。
赋值运算符与右侧相关联这一事实并不引人注目,但出于对历史的好奇,值得注意。当 Bjarne Stroustrup 在四处寻找操作符以重载 I/O 流时,他选择了 << 和 >>,因为尽管赋值操作符可能更自然 [1],但赋值绑定到右侧,并且流运算符必须绑定到左侧(std::cout << a << b 必须是 (std::cout << a) << b)。但是,由于 << 绑定比赋值更紧密,因此使用流式运算符时有许多陷阱。(最近让我抓到的一个是这种移位比按位运算符绑定得更紧密。)
[注 1]:我没有对此的引用,但我记得很多年前在C++ 编程语言中阅读过它。我记得,对于赋值运算符是否自然并没有达成共识,但似乎比重载移位运算符更自然,使其与正常语义完全不同。