最令人烦恼的解析

最令人烦恼的解析 （most vexing parse）是C++编程语言中的一种反直觉的二义性解析形式。 在一些场景下，编译器无法区分某语句是初始化时某对象的参数，还是声明一个函数时指定参数类型。在这些情况下，编译器将该行解释为函数声明。 术语"most vexing parse" 最初由Scott Meyers用于2001年的书籍 Effective STL中。 尽管在C语言中不常见，但这种现象在 C++ 中很常见，直到C++11推出了统一初始化才得以解决。 一个例子如下： void f(double my_dbl) { int i(int(my_dbl)); } 上面的第 2 行是有歧义的。一种可能的解释是声明一个变量 i ，初始值通过转换my_dbl 到一个int而来。但是，C 允许在函数参数声明周围使用多余的括号；因此，声明的i实际上等同于以下代码： // A function named i takes an integer and returns an integer. int i(int my_dbl); 一个更可能出现的例子是： struct Timer {}; struct TimeKeeper { explicit TimeKeeper(Timer t); int get_time(); }; int main() { TimeKeeper time_keeper(Timer()); return time_keeper.get_time(); } 其中 TimeKeeper time_keeper(Timer()); 是有歧义的，它可以被解释为： 一个变量：定义为类TimeKeeper的变量time_keeper，用类Timer的匿名实例初始化。 一个函数声明：声明了一个函数time_keeper，返回一个TimeKeeper，有一个（未命名的）参数。参数的类型是一个（指向）不接受输入并返回Timer对象的函数（的指针） 。 C ++标准采取第二种解释，这与上面的第9行不一致。例如，Clang++警告第9行存在最令人烦恼的解析，并报错： $ clang++ time_keeper.cc timekeeper.cc:9:25: warning: parentheses were disambiguated as a function declaration [-Wvexing-parse] TimeKeeper time_keeper(Timer()); ^~~~~~~~~ timekeeper.cc:9:26: note: add a pair of parentheses to declare a variable TimeKeeper time_keeper(Timer()); ^ ( ) timekeeper.cc:10:21: error: member reference base type 'TimeKeeper (Timer (*)())' is not a structure or union return time_keeper.get_time(); ~~~~~~~~~~~^~~~~~~~~ 这些有歧义的声明往往不会被解析为程序员所期望的语句。 C++ 中的函数类型通常隐藏在typedef之后，并且通常具有显式引用或指针限定符。要强制扭转解析的结果，常见做法是换一种不同的对象创建或转换语法。 在类型转换的示例中，有两种替代语法：“C 风格强制类型转换” // declares a variable of type int int i((int)my_dbl); 或一个static_cast转换： int i(static_cast<int>(my_dbl)); 在变量声明的示例中，首选方法（自 C++11 起）是统一（大括号）初始化。 这也允许完全省略类型名称： //Any of the following work: TimeKeeper time_keeper(Timer{}); TimeKeeper time_keeper{Timer()}; TimeKeeper time_keeper{Timer{}}; TimeKeeper time_keeper( {}); TimeKeeper time_keeper{ {}}; 在 C++11 之前，强制获得预期解释的常用手段是使用额外的括号或拷贝初始化： TimeKeeper time_keeper( /*Avoid MVP*/ (Timer()) ); TimeKeeper time_keeper = TimeKeeper(Timer()); 后一种写法中，拷贝赋值运算符 有可能被编译器优化。 自C++17开始，这种优化受到保证。 根据C++类型退化规则，作为参数声明的函数等价于一个指向同类型函数的指针。参见C++函数对象的实例。 Meyers, Scott. Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library. Addison-Wesley. 2001. ISBN 0-201-74962-9.  Coffin, Jerry. c++ - What is the purpose of the Most Vexing Parse?. Stack Overflow. 29 December 2012 [2021-01-17]. （原始内容存档于2021-11-25）.  Lattner, Chris. Amazing Feats of Clang Error Recovery. LLVM Project Blog. The Most Vexing Parse. 5 April 2010 [2021-01-17]. （原始内容存档于26 September 2020）.  DrPizza; Prototyped; wb; euzeka; Simpson, Homer J. C++'s "most vexing parse". ArsTechnica OpenForum. October 2002 [2021-01-17]. （原始内容存档于2021-11-25）.  Boccara, Jonathan. The Most Vexing Parse: How to Spot It and Fix It Quickly. Fluent C++. 2018-01-30 [2021-01-17]. （原始内容存档于2021-11-25） （美国英语）.  Stroustrup, Bjarne. C++11 FAQ. www.stroustrup.com. Uniform initialization syntax and semantics. 19 August 2016 [2021-01-17]. （原始内容存档于2021-08-20） （英语）.  Myths and urban legends about C++. C++ FAQ. What is copy elision? What is RVO?. [2021-01-17]. （原始内容存档于2021-11-25）.  Devlieghere, Jonas. Guaranteed Copy Elision. Jonas Devlieghere. 2016-11-21 [2021-01-17]. （原始内容存档于2021-11-25） （英语）.  Note, however, the caveats covered in Brand, C++. Guaranteed Copy Elision Does Not Elide Copies. Microsoft C++ Team Blog. 2018-12-11 [2021-01-17]. （原始内容存档于2021-11-25） （美国英语）.  Discussion in the C++03 standard final draft (see §8.2 Ambiguity resolution [dcl.ambig.res]): https://web.archive.org/web/20141113085328/https://cs.nyu.edu/courses/fall11/CSCI-GA.2110-003/documents/c++2003std.pdf CppReference on direct initialization (the sort vulnerable to the most vexing parse): https://en.cppreference.com/w/cpp/language/direct_initialization （页面存档备份，存于互联网档案馆）