C++'s most vexing parse 是 Scott Meyers 在其名著《Effective STL》中创造的一个术语。
Scott 用这个术语来形容 C++ 标准对于 declaration 语句的消歧义(ambiguity resolution)约定与常人的认知相悖。
形如 Type()
或 Type(name)
的表达在某些情况下具有歧义(syntax ambiguity)。
一
T1 name(T2());
这是一个 declaration statement。
既可视作声明了一个类型为 T1
名为 name
的 object,并且用一个类型为 T2
的 object 作为其 initilizer(即标准中所谓「an object declaration with a function-style cast as the initializer」),也可视作声明了一个返回值类型为 T1
名为 name
的函数,此函数有一个参数,参数类型为「指向返回值类型为 T2
,参数为空的函数的指针」。
C++ 标准规定把这样的 statement 视作函数声明。
二
T1 name1(T2(name2));
根据 C++ 标准,此时不把 T2(name2)
视为「a function style cast」,而将其视为 T2 name2
,这样整个语句就变成
T1 name1(T2 name2);
,显然这是个函数声明。
类似地,
T1 name1(T2(name2), T3(name3));
被视作 T1 name1(T2 name2, T3 name3);
C++ 标准将上述两种情况总结为
..., the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. ..., the resolution is to consider any construct that could possibly be a declaration a declaration... A declaration can be explicitly disambiguated by adding parentheses around the argument. The ambiguity can be avoided by use of copy-initialization or list-initialization syntax, or by use of a non-function-style cast.
并给出了例子
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
}
不难看出,the most vexing parse 的根源在于default constructor、converting constructor 和 conversion operator。
converting constructor 是指调用时只需一个实参的 constructor。From C++ Primer (5th edition):
Every constructor that can be called with a single argument defines an implicit conversion to a class type. Such constructors are sometimes referred to as converting constructors.
C++ Primer 上关于 conversion operator 的内容
A conversion operator is a special kind of member function that converts a value of a class type to a value of some other type. A conversion function typically has the general form
operator T() const;
where T
represents a type.
Conversion operators have no explicitly stated return type and no parameters, and they must be defined as member functions. Conversion operations ordinarily should not change the object they are converting. As a result, conversion operators usually should be defined as const
members.