C++'s most vexing parse

原文::http://zkt.name/c-s-most-vexing-parse/

https://en.wikipedia.org/wiki/Most_vexing_parse

C++'s most vexing parse

最近同事在使用C++的时候遇到一个诡异的问题,初始化一个对象的时候构造函数没有被调用。类似的代码如下:

#include <iostream>
class A {  
  public:
     A(const std::string& name){
        std::cout << name << std::endl;
     }
};
int main()  
{
    char szTmp[] = "Hello";
    A a(std::string(szTmp));
    return 0;
}

这段代码非常简单,意图就是构造一个std::string匿名对象,然后传递给类A的构造函数,构造函数输出这个字符串。但是这段代码什么都没输出,说明构造函数没有被执行。这个奇怪的结果让我很好奇。

经过一番资料查阅,原来A a(std::string(szTmp));这段代码被解析成了一个函数名为a有一个std::string参数szTmp并返回A类型的函数声明。这在Scott Meyers的《Effective STL》有做解释,并把这个问题称为C++'s most vexing parse,本文也用了这个标题,翻译为C++最令人费解的解析。

在C++中,以下三种写法都声明了同一个函数

int f(double d)//声明接受一个double参数d,返回值为int类型的函数  
int f(double (d));//效果一样,参数名外的括号会被忽略  
int f(double)//直接省略参数名  

类似的,以下三种写法都声明的函数也相同

int g(double (*pf)()); //声明接受一个无参数返回类型为double的函数指针pf参数,返回值为int类型的函数  
int g(double pf());//效果一样,pf是隐式函数指针  
int g(double ());//直接省略参数名  

前面代码中的A a(std::string(szTmp));其实就跟函数f的第二种声明方式,szTmp两边的括号被忽略。然后被解析成一个函数声明。还有一种情况,如果这段这段代码改成A a();,也不会调用A的默认构造函数,同样会被解析成函数声明。 这确实是一个违反直觉的解析方式,所以在C++11中,针对这种情况,有提出解决方案。

Scott在书中有提到一种解决方法,就是把整个匿名对象用括号括起来,就像这样A a((std::string(szTmp)));。更好地做法还是避免写这样的代码,而是先在外面初始化一个std::string类型的变量,然后再传给构造函数。或者直接通过隐式转换A a(szTmp);来创建对象。

在C++11中,使用Uniform initialization可以处理这种歧义,

Uniform initialization syntax

Using the new uniform initialization syntax introduced in C++11 solves this issue.

The problematic code is then unambiguous when braces are used:

  TimeKeeper time_keeper{Timer{}};

使用新的语法可以这样写A a{std::string(szTmp)};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值