最烦人的解析

Neo*_*low 8 c++ most-vexing-parse

我在Cpp Quiz 上看到了一个代码[问题 #38]

#include <iostream>

struct Foo
{
  Foo(int d) : x(d) {}
  int x;
};

int main() 
{ 
  double x = 3.14;

  Foo f( int(x) );

  std::cout << f.x << std::endl;

  return 0;
} 
Run Code Online (Sandbox Code Playgroud)

据说这段代码格式不正确,因为Foo f( int(x) );将被视为函数声明而不是 type 的对象声明Foo

据我所知,这是“最烦人的解析”的一个实例。我的问题是int(x)语句中的这种语法是什么Foo f( int(x) );意思?到目前为止,我只看到了如下函数声明:

  1. Foo f( int );

  2. Foo f( int x );

是一样的Foo f( int x );吗?

son*_*yao 7

int(x)语句中的这个语法Foo f( int(x) );是什么意思?

周围的括号x是多余的,将被忽略。Soint(x)int x这里一样,表示一个以xtype命名的参数int

是一样的Foo f( int x );吗?

是的。Foo f( int(x) );, 是一个函数声明,它被命名f,返回Foo,接受一个以xtype命名的参数int

这是标准的解释。[dcl.ambig.res]/1 :

(强调我的)

由函数样式转换和 [stmt.ambig] 中提到的声明之间的相似性引起的歧义也可能出现在声明的上下文中。在这种情况下,选择是在参数名称周围带有一组冗余括号的函数声明和将函数样式强制转换为初始值设定项的对象声明之间进行选择。正如 [stmt.ambig] 中提到的歧义一样,解决方案是考虑任何可能是声明的构造

注意:可以通过在参数周围添加括号来明确消除声明的歧义。可以通过使用复制初始化或列表初始化语法,或使用非函数式强制转换来避免歧义。

struct S {
  S(int);
};

void foo(double a) {
  S w(int(a));      // function declaration
  S x(int());       // function declaration
  S y((int(a)));    // object declaration
  S y((int)a);      // object declaration
  S z = int(a);     // object declaration
}
Run Code Online (Sandbox Code Playgroud)

因此,int(x)将被视为(参数的)声明而不是函数样式转换。


650*_*502 5

问题是,由于我不知道的原因,将参数名称包装在原型中的括号中是有效的。所以

Foo f(int(x));
Run Code Online (Sandbox Code Playgroud)

可以解释为

Foo f(int x);
Run Code Online (Sandbox Code Playgroud)

这被认为是

Foo f(int);
Run Code Online (Sandbox Code Playgroud)

然而,真正的问题是 C++ 作者,也出于我不知道的原因,决定为几乎完全相同的语义(实例初始化)使用两种不同的语法形式是很酷的。

这引入了一种语法歧义,通过说“如果某物既可以是声明又是定义,那么它就是声明”就可以“解决”,从而触发陷阱。

因此,C++ 解析器必须能够解析任意数量的标记,然后才能确定其中第一个的语义含义。

除了编译器编写者之外,这显然不会成为太大的问题,但这意味着阅读 C++ 代码以理解它的人也必须能够做到这一点,而对于我们人类来说,这更难。从此“最烦人”。