C++语法分析中最让人头疼的歧义
C++是个特别复杂的语言,其复杂性不仅体现在开发模式上,也体现在语法分析上。许多人都遇到过嵌套模板参数的歧义问题,如vector<vector<int>> v
,在有些编译器上会被解析为vector < vector < int >> v
,但新的编译器都已经解决了。而最让人头疼的歧义则是Most vexing parse:
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
类型的对象,并用Timer()
作为初始化参数。 - 声明一个名叫
time_keeper
的函数,它的返回值类型是TimeKeeper
,参数是一个函数指针,这个函数指针指向的函数的返回值是Timer
,无参数。
很明显我们想要表达的是第一种意思,但很不幸编译器会默认理解为第二种。Clang++会给出以下错误:
timekeeper.cc:15:21: error: member reference base type 'TimeKeeper (Timer (*)())' is not a
structure or union
return time_keeper.get_time();
~~~~~~~~~~~^~~~~~~~~
之所以产生这种歧义,是因为这几个原因:
- C++的函数在使用前需要声明,定义和声明是可以分离的。
- C++的函数声明的参数可以只有类型,没有名称,如
int max(int, int);
。 - C++的函数声明的参数名在类型名后可以加
()
,如int max(int (a), int())
。 - C++的函数声明可以在函数体中。
最优美的解决方案是使用C++11的统一初始化语法:
TimeKeeper time_keeper{Timer()};