C++ Most vexing parse(C++最头疼的解析)

46 篇文章 4 订阅

首先需要了解,在C++中,如下三种方式声明了同一个函数:

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

同样地,如下三种方法也声明了同一个函数:

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

对于如下示例:

#include <iostream>

class Timer
{
public:
    Timer();
};

class TimeKeeper
{
public:
    TimeKeeper(const Timer& t);

    int get_time() const;
};

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

这段代码编译通不过,在visual studio上报错:

error: request for member 'get_time' in 'time_keeper', which is of non-class type 'TimeKeeper(Timer (*)())'
   return time_keeper.get_time();

问题出在

 TimeKeeper time_keeper(Timer());

该行可以有两种解读:

1. 定义了一个类型为TimeKeeper的对象time_keeper,用一个匿名的类型为Timer的实例初始化。

2. 声明了一个返回类型为TimeKeeper,函数名为time_keeper,有一个匿名参数,参数类型为指向返回类型为Timer,参数为空的函数指针。

绝大部分程序员期望是第一种情况,但是C++标准把这样的声明视为第二种,既函数声明。

解释如下:

T1 name1(T2(name2));

根据 C++ 标准,此时不把 T2(name2) 视为「a function style cast」,而将其视为 T2 name2,这样整个语句就变成
T1 name1(T2 name2);,显然这是个返回类型为T1,参数类型为T2的函数声明。

类似地,
T1 name1(T2(name2), T3(name3)); 被视作 T1 name1(T2 name2, T3 name3);

C++ 标准将上述两种情况总结为

.., the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. ..., the resolution is to consider any construct that could possibly be a declaration a declaration... A declaration can be explicitly disambiguated by adding parentheses around the argument. The ambiguity can be avoided by use of copy-initialization or list-initialization syntax, or by use of a non-function-style cast.

并给出了例子

struct S {
  S(int);
};

void foo(double a) {
  S w(int(a));                  // function declaration
  S x(int());                   // function declaration
  S y((int(a)));                // object declaration
  S y((int)a);                  // object declaration
  S z = int(a);                 // object declaration
}

不难看出,the most vexing parse 的根源在于default constructor、converting constructor 和 conversion operator。

converting constructor 是指调用时只需一个实参(或者多个参数但是只有第一个参数没有默认值)的 constructor。

conversion operator把类类型转换为T类型,如:operator T() const;

解决方法

1. 添加额外的一对括号, TimeKeeper time_keeper((Timer()));

2. 使用复制初始化, TimeKeeper time_keeper = TimeKeeper(Timer());

3. 从C++11起,使用统一的初始化:

    a. TimeKeeper time_keeper{Timer()};

    b. TimeKeeper time_keeper(Timer{});

    c. TimeKeeper time_keeper{Timer{}};

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值