β

C++'s most vexing parse

ZKT编程与生活 18 阅读
C++

最近同事在使用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)};

C++
作者:ZKT编程与生活
专注Linux环境下的C/C++编程,关注移动互联网和WEB开发相关技术。
原文地址:C++'s most vexing parse, 感谢原作者分享。

发表评论