C++'s most vexing parse

来源:互联网 发布:51微投票系统源码 编辑:程序博客网 时间:2024/06/06 16:30

转载至:http://www.zkt.name/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可以处理这种歧义,使用新的语法可以这样写A a{std::string(szTmp)};

Kuntzuo