Most vexing parse 在 Scott Meyers 在 Effective STL(2001)中 首次使用 。它在 C++ language standard 的 8.2节中正式定义。
Example with classes
An example is:
#include <iostream>
class Timer {
public:
Timer(){};
};
class TimeKeeper {
public:
TimeKeeper(const Timer& t){};
int get_time(){return 0;};
};
int main() {
TimeKeeper time_keeper(Timer());
return time_keeper.get_time();
}
TimeKeeper time_keeper(Timer());
似乎模棱两可,因为它可以解释为
- 一个类型为
TimeKeeper
的变量名为time_keeper
,用Timer
类的匿名实例初始化 - 一个函数名为
time_keeper
的函数声明,返回值类型为TimeKeeper
,并具有一个(未命名)参数,该参数是指向函数的指针,该函数返回Timer类型的对象(并且不输入任何内容) (See Function object #In C and C++#)
大多数程序员期望第一个,但是C ++标准要求将其解释为第二个。例如,g++ 给出以下错误消息:
$ g ++ -c time_keeper.cc time_keeper.cc:在函数’int main()‘中:
time_keeper.cc : 15:错误:请求’time_keeper’中的成员’get_time’,它
是非类类型’TimeKeeper(Timer) (*)())’
请注意,编译器会给出有关return
语句的错误消息main()
:由于将的声明解释time_keeper
为函数声明,因此我们将无法对此调用成员函数get_time()
。
强制编译器将其视为变量定义的常见方法是:
- 要添加一对额外的括号:
TimeKeeper time_keeper((Timer()));
- 要使用副本初始化:
TimeKeeper time_keeper = TimeKeeper(Timer());
- (在C ++ 11和更高版本中。)对括号使用初始化列表初始化:
TimeKeeper time_keeper{Timer()};
TimeKeeper time_keeper(Timer{});
TimeKeeper time_keeper{Timer{}};
Example with functions
当函数强制转换以 初始化变量 或 传递给构造函数参数 时,函数将产生Most vexing parse 。
void f(double adouble) {
int i(int(adouble)); //旨在强制转换以初始化变量
}
在这种情况下,在double
周围的括号是多余的,并且i
的声明和一个等效于以下内容的函数声明相同
// 接受一个int并返回一个int的函数声明
int i(int adouble);
为了消除这一点,有利于变量声明,可以使用与上述第一种情况相同的技术。另一种解决方案是使用强制转换符号:
//声明一个名为“ i”的变量
int i((int) adouble);
或者也可以使用 named cast :
// declares a variable called 'i'
int i(static_cast<int>(adouble));
Uniform initialization syntax (统一初始化语法)
使用C ++ 11中引入的新的初始化列表可以解决此问题。 当使用花括号时,有问题的代码将是明确的:
TimeKeeper time_keeper{Timer{}};
如上所述使用花括号为TimeKeeper
类的变量time_keeper
创建变量定义,并使用Timer
类的匿名实例进行初始化。