Beyond the Void
BYVoid
C++语法分析中最让人头疼的歧义
本文简化字版由OpenCC转换

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());,因为它有两种理解方式:

  1. 定义一个TimeKeeper类型的对象,并用Timer()作为初始化参数。
  2. 声明一个名叫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();
          ~~~~~~~~~~~^~~~~~~~~

之所以产生这种歧义,是因为这几个原因:

  1. C++的函数在使用前需要声明,定义和声明是可以分离的。
  2. C++的函数声明的参数可以只有类型,没有名称,如int max(int, int);
  3. C++的函数声明的参数名在类型名后可以加(),如int max(int (a), int())
  4. C++的函数声明可以在函数体中。

最优美的解决方案是使用C++11的统一初始化语法:

TimeKeeper time_keeper{Timer()};

上次修改时间 2017-03-16

相关日志