C++'s most vexing parse
C++’s most vexing parse 是 Scott Meyers 在其名著《Effective STL》中创造的一个术语。Scott 用这个术语来形容 C++ 标准对于 declaration 语句的消歧义(ambiguity resolution)约定与常人的认知相悖。
最令人烦恼的解析 (most vexing parse)是C++中的一种反直觉的二义性解析形式。 在一些场景下,编译器无法区分某语句是初始化时某对象的参数,还是声明一个函数时指定参数类型。在这些情况下,编译器将该行解释为函数声明。
形如 Type()
或 Type(name)
的表达在某些情况下具有歧义(syntax ambiguity)。
C风格强制类型转换
1 | void f(double my_dbl) { |
上面的第 2 行是有歧义的。一种可能的解释是声明一个变量i
,初始值通过转换my_dbl
到一个int
而来。但是,C
允许在函数参数声明周围使用多余的括号;因此,声明的i实际上等同于以下代码:
1 | // A function named i takes an integer and returns an integer. |
未命名的临时对象
1 | struct Timer {}; |
其中
1 | TimeKeeper time_keeper(Timer()); |
是有歧义的,它可以被解释为:
- 一个变量:定义为类
TimeKeeper
的变量time_keeper
,用类Timer
的匿名实例初始化。 - 一个函数声明:声明了一个函数
time_keeper
,返回一个TimeKeeper
,有一个(未命名的)参数。参数的类型是一个(指向)不接受输入并返回Timer
对象的函数(的指针)。
[C ++标准]采取第二种解释,这与上面的第9行不一致。例如,Clang++
警告第9行存在最令人烦恼的解析,并报错:
1 | clang++ time_keeper.cc |
解决方案
这些有歧义的声明往往不会被解析为程序员所期望的语句。C++ 中的函数类型通常隐藏在typedef
之后,并且通常具有显式引用或指针限定符。要强制扭转解析的结果,常见做法是换一种不同的对象创建或转换语法。
在类型转换的示例中,有两种替代语法:“C 风格强制类型转换”
1 | // declares a variable of type int |
或一个static_cast转换:
1 | int i(static_cast<int>(my_dbl)); |
在变量声明的示例中,首选方法(自 C++11 起)是统一(大括号)初始化。 这也允许完全省略类型名称:
1 | //Any of the following work: |
在 C++11 之前,强制获得预期解释的常用手段是使用额外的括号或拷贝初始化:
1 | TimeKeeper time_keeper( /*Avoid MVP*/ (Timer())); // 增加一个括号 |
本文标题:C++'s most vexing parse
文章作者:王二
发布时间:2023-08-09
最后更新:2024-11-06
原始链接:https://wanger-sjtu.github.io/most-vexing-parse/
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!