C++ most vexing parse(C++最令人烦恼的解析)

介绍与引入

最近在阅读C++ Concurrency in Action 2rd edition,在里面看到一个很有意思的说法叫做most vexing parse,是有关于C++中一种违反直觉的语法歧义解析现象。在某些情况下,C++语法解析器无法区分以下二者

  1. The creation of an object parameter,即对象参数的创建
  2. The specification of a function's type,即函数类型的规约。

发生歧义时,编译器解释为第2种情况,该现象也被称为most vexing parse,这种现象直到C++11 引入uniform initialization后才得到解决。

例一:C风格类型转换

考虑我们要进行一次C风格的强制类型转换

void f(double my_dbl) {
  int i(int(my_dbl));
}

第2行就有着多种解释。

  1. 新建一个int型变量,并用强制类型转换的my_db1进行初始化 。
  2. 声明一个形如下的函数 ,这是由于C语言允许参数被多余的括号包裹着。
int i (int my_db1);

例二:匿名的临时变量

一个更加详细且更常见的例子为:

struct Timer {};

struct TimeKeeper {
  explicit TimeKeeper(Timer t);
  int get_time();
};

int main() {
  TimeKeeper time_keeper(Timer());
  return time_keeper.get_time();
}

对于main函数中的

TimeKeeper time_keeper(Timer());

也有着多种解释。

  1. 声明一个变量time_keeper为类TimeKeeper的实例,且用一个匿名类Timer()初始化
  2. 声明一个函数time_keeper,其有一个匿名参数,参数类型为“一个无参数的,返回值类型为Timer对象的函数指针”,返回值类型为TimeKeeper的类对象。

C++标准采用第二种解释,则main函数的返回

return time_keeper.get_time();

将类型不匹配。编译器选择g++,在CLion中会有如下错误提示:

可见一个好的IDE真的很重要(逃),否则编译期间也会报错。

解决方法

对于例一,有2种解决方法。

第一种,便是使用不一样的强制类型转换语法(实际上就是换个括号位置)

int i((int)my_dbl);

或者使用named cast

int i(static_cast<int>(my_dbl));

对于例二,在变量的声明中,首选方法(自C++11开始)是uniform initialization(即使用大括号)。以下任意一种方式都是正确的

TimeKeeper time_keeper(Timer{});
TimeKeeper time_keeper{Timer()};
TimeKeeper time_keeper{Timer{}};
TimeKeeper time_keeper(     {});
TimeKeeper time_keeper{     {}};

在 C++11 之前,常用的方法是使用额外的括号或复制初始化

TimeKeeper time_keeper((Timer()));
TimeKeeper time_keeper = TimeKeeper(Timer());

结语

尽管现在的IDE已经很智能了,可是难免还是会面临着不能用IDE的场景,有时候了解一下这个也挺有意思的,就没听说过Java还有这种歧义(当然也是我孤陋寡闻)。

参考文献

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值