1

从 C++ 标准来看,赋值表达式的语法是这样的:

assignment-expression:
  conditional-expression
  logical-or-expression assignment-operator assignment-expression
  throw-expression

assignment-operator: one of
   = *= /= %= += -= >>= <<= &= ^= |=

请注意,“赋值运算符”的左侧是“逻辑或表达式”,即类似于(4 || localvar1) = 5 ; 是根据语法的有效赋值表达式。这对我来说没有意义。为什么他们选择“逻辑或表达式”而不是标识符或 id_expression?

4

3 回答 3

2

语法有点复杂,但如果你继续展开前面的定义,你会发现赋值表达式非常通用,几乎可以处理任何事情。虽然您引用的标准中的片段侧重于logical-or-expression,但如果您继续展开其定义,您会发现赋值的左侧和右侧几乎都可以是任何子表达式(尽管不是字面意思任何)。

前面指出的原因是赋值可以应用于枚举或基本类型的任何左值表达式或类类型的右值表达式(其中operator=始终是成员)。许多表达式,在允许运算符重载并且不定义从运算符返回的类型是什么的语言中,可以潜在地满足赋值的需要,并且语法必须允许所有这些用途。

标准中的不同规则稍后将限制可以从语法生成的哪些可能的表达式实际上是有效的或无效的。

于 2013-08-22T13:15:24.910 回答
2

您的特定语句(4 || localvar1) = 5;无效(除非operator||重载),因为您不能将 5 分配给 4 (4 是r-value)。您必须在左侧有一个左(可以分配的东西),例如函数返回的引用。

例如,假设您有一些函数int& get_my_int()返回对整数的引用。然后,您可以这样做:

`get_my_int() = 5;`

这将设置返回的整数get_my_int()to 5。就像在您的第一篇文章中一样,这必须是对整数的引用(而不是值);否则,上述语句将无法编译。

于 2013-08-22T13:07:22.967 回答
1

实际上,关于赋值语句的 C++ 语法有两件有趣的事情,它们都与以下内容的有效性无关:

 (4 || localvar1) = 5;

由于括号,该表达式在语法上是有效的(直到类型检查)。引用类型的任何带括号的表达式在赋值运算符的左侧在语法上都是正确的。(而且,正如已经指出的那样,几乎任何涉及用户类型或函数的表达式都可以是引用类型,这是运算符重载的结果。)

语法更有趣的是它建立了赋值运算符的左优先级低于几乎所有其他运算符,包括逻辑或,因此上述表达式在语义上等价于

4 || localvar1 = 5;

即使许多读者将上述解释为4 || (localvar1 = 5)(假设它localvar1是可以由 an 分配的类型,这将是完全正确的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)

这里 the,是一个逗号操作符,它显然需要比围绕它的任何一个赋值更紧密地绑定。(C 和 C++ 仅在此语法中例外,因为它具有逗号运算符;在大多数语言中,,不被视为运算符。)此外,显然不希望将第一个赋值表达式解析为(first = p) = vec.begin().

赋值运算符与右侧相关联这一事实并不引人注目,但出于对历史的好奇,值得注意的是。当 Bjarne Stroustrup 正在四处寻找运算符以重载 I/O 流时,他选择了<<>>因为尽管赋值运算符可能更自然 [1],但赋值绑定到右侧,而流式运算符必须绑定到左侧(std::cout << a << b必须是(std::cout << a) << b)。但是,由于<<绑定比赋值更紧密,因此在使用流式运算符时存在许多问题。(最近让我感到困惑的是,移位比按位运算符绑定得更紧密。)

[注 1]:我没有对此的引用,但我记得很多年前在The C++ Programming Language中读过它。我记得,关于赋值运算符是否自然并没有达成共识,但似乎比重载移位运算符更自然,使其与正常语义完全不同。

于 2013-08-22T14:25:49.907 回答