首先需要了解,在C++中,如下三种方式声明了同一个函数:
int f(double d); //声明接受一个double参数d,返回值为int类型的函数
int f(double (d));//效果一样,参数名外的括号会被忽略
int f(double);//直接省略参数名
同样地,如下三种方法也声明了同一个函数:
int g(double (*pf)()); //声明返回值为int类型的函数,接受一个返回类型为double无参数的函数指针pf
int g(double pf());//效果一样,pf是隐式函数指针
int g(double ());//直接省略参数名
对于如下示例:
#include <iostream>
class Timer
{
public:
Timer();
};
class TimeKeeper
{
public:
TimeKeeper(const Timer& t);
int get_time() const;
};
int main()
{
TimeKeeper time_keeper(Timer());
return time_keeper.get_time();
}
这段代码编译通不过,在visual studio上报错:
error: request for member 'get_time' in 'time_keeper', which is of non-class type 'TimeKeeper(Timer (*)())'
return time_keeper.get_time();
问题出在
TimeKeeper time_keeper(Timer());
该行可以有两种解读:
1. 定义了一个类型为TimeKeeper的对象time_keeper,用一个匿名的类型为Timer的实例初始化。
2. 声明了一个返回类型为TimeKeeper,函数名为time_keeper,有一个匿名参数,参数类型为指向返回类型为Timer,参数为空的函数指针。
绝大部分程序员期望是第一种情况,但是C++标准把这样的声明视为第二种,既函数声明。
解释如下:
T1 name1(T2(name2));
根据 C++ 标准,此时不把 T2(name2)
视为「a function style cast」,而将其视为 T2 name2
,这样整个语句就变成T1 name1(T2 name2);
,显然这是个返回类型为T1,参数类型为T2的函数声明。
类似地,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。
conversion operator把类类型转换为T类型,如:operator T() const;
解决方法
1. 添加额外的一对括号, TimeKeeper time_keeper((Timer()));
2. 使用复制初始化, TimeKeeper time_keeper = TimeKeeper(Timer());
3. 从C++11起,使用统一的初始化:
a. TimeKeeper time_keeper{Timer()};
b. TimeKeeper time_keeper(Timer{});
c. TimeKeeper time_keeper{Timer{}};