运算符优先级,文档不一致

Operator precedence, inconsistent documentations

我正在刷新我对运算符优先级的记忆,因为我尽量做个聪明人并尽可能避免括号,刷新以下 2 个链接:

cpp reference

MS docs

我遇到的一个问题是,那两个 "reliable" 文档讲的不是同一件事,我不知道该相信谁了?

例如,Cppreference 表示 throw 关键字与条件运算符在同一组中。 Microsoft 的文档说条件运算符高于 throw。还有其他区别。

哪个网站是正确的,或者这两个网站在不同方面都是错误的?

TL;DR:Microsoft 文档可能被解释为不太正确,具体取决于您如何看待它们。

您首先要了解的是 C++ 作为一种语言没有 "operator precedence" 规则。 C++有一个语法;正是语法定义了一段特定的 C++ 语法的含义。 C++ 语法告诉你 5 + 3 * 4 应该被认为等同于 5 + (3 * 4) 而不是 (5 + 3) * 4.

因此,您看到的任何 "operator precedence" 规则只是对C++ grammar around expression parsing 的文本、人类可读的解释。因此,可以想象可能存在两种不同的方式来描述同一语法的行为。

考虑 throw?: 运算符的具体示例。 Microsoft 站点表示 ?: 的优先级高于 throw,而 Cppreference 站点表示它们具有相同的优先级。

首先,让我们看一个假设的 C++ 表达式:

throw val ? one : two

根据 Microsoft 的规则,?: 运算符具有更高的优先级,因此将被解析为 throw (val ? one : two)。根据 Cppreference 的规则,这两个运算符具有相同的优先级。但是,由于它们具有从右到左的关联性,因此 ?: 在子表达式上获得优先权。所以我们有 throw (val ? one : two).

所以他们都解决了相同的结果。

但是 C++ 语法是怎么说的呢?好吧,这是语法的相关片段:

throw-expression:
  throw  assignment-expression(opt)

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

这被解析为一个抛出表达式,其中包含一个 assignment-expression,其中包含一个 conditional-expression,这就是我们的 ?: 所在的位置。简而言之,解析器将其解析为 throw (val ? one : two).

所以这两个页面是一样的,而且都是正确的。

现在考虑:

val ? throw one : two

这是如何解析的?好吧,要记住 ?: 是一个三元运算符;与大多数其他人不同,它具有三个术语。也就是说,在 : <something> 被解析之前,conditional-expression 本身并未被指定 finished

因此 throw?: 的优先级在这种情况下无关紧要。 throw one 在三元运算符内,因为表达式 字面上在 三元运算符内。两家运营商没有竞争。

最后,怎么样:

val ? one : throw two

Microsoft 给予 ?: 更高的优先级。根据微软的文档,优先级"specifies the order of operations in expressions that contain more than one operator"。所以 ?: 先发生。

问题就在这里。 throw 本身实际上是一个语法上合法的表达式(它仅在 catch 子句中有效的 C++,但语法在任何地方都是合法的)。因此,val ? one : throw 可能是一个合法的表达式,这就是 Microsoft 文档的规则似乎要表达的意思。

当然,(val ? one : throw) two 不是合法的表达式,因为 () two 不是合法的 C++ 语法。因此,可以将 Microsoft 的规则解释为这应该是一个编译错误。

但事实并非如此。 C++ 的语法状态:

conditional-expression:
  logical-or-expression
  logical-or-expression ? expression : assignment-expression

throw two 是用作给定表达式的第三个操作数的完整 assignment-expression。所以这应该被解析为 val ? one : (throw two).

那么 Cppreference 呢?好吧,通过赋予它们从右到左的关联性,throw two 与自身组合在一起。所以应该考虑val ? one : (throw two).