在class中初始化vector成员发生的most vexing parse问题

前言

在class中带容量初始化vector时,发生报错,Expected parameter declaratorExpected ')',我寻思着我括号都敲对了啊,怎么就出问题了?加上初始值,还是报错,然后准备删掉初始值和size时,才发现我好像声明了个函数……真坑,规范编程太重要了。

class Solution {
private:
    // vector<int> v1(10); // 错误
    // vector<int> v1(10, 0); // 错误
    // vector<int> v1(); // 仔细一看,这不是声明了个空参返回vector的v1函数吗!所以前两行错误的原因是编译器识别v1为函数而10又不是任何一种type
	[...]
}

有个术语专门指代这种情况:most vexing parse,即最令人烦恼的解析,是C++中一种反直觉的二义解析形式。简单说就是,在编译器无法区分某语句是初始化对象参数还是声明一个函数时指定参数类型时,就会将该行解释为函数声明。除了初始化vector以外还有好多情况会出现这种MVP。

先说结论

那么如何这样带参数初始化呢?

C++11以后,有了统一初始化(Uniform initialization),可以用大括号完成我们想要的效果,此外还有第二种赋值初始化。

class Solution {
private:
	vector<int> v1{vector<int>(10, 0)}; // 第二个参数缺省为0
    vector<int> v2 = vector<int>(10, 0); // 第二个参数缺省为0
	[...]
}

需要注意:vector<int> v1{10}是按元素初始化,只会初始化一个元素10;

在C++11之前,就没有这种初始化方式了,我们必须老老实实在构造函数里面初始化,可以用初始化列表或者vector的resize函数:

// 方法一:
class Solution {
private:
    vector<int> v1;
	[...]
public:
    Solution() : v1(10, 0) {} // 在构造函数初始化列表里面初始化
}

// 方法二:
class Solution {
private:
    vector<int> v1;
	[...]
public:
    Solution() {
        v1.resize(10, 0); // 在构造函数中用resize函数
    }
}

从兼容性来说,还是方法二最合适。

细说一下MVP

好,我们不能只满足于解决了初始化问题,来细看一下这个MVP到底是个啥,以及如何避免。

看Wiki给的一个例子:

struct Timer {};

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

int main() {
  TimeKeeper time_keeper(Timer()); // 这里有歧义
  return time_keeper.get_time();
}

在代码中标注一行中的time_keeper可以有两种解释:

  1. 一个TimeKeeper类型的变量,用类Timer的默认构造函数初始化;这也是我们想实现的

  2. 一个函数time_keeper,返回值是TimeKeeper类型,有一个函数参数,等价为一个指针参数,该指针指向Timer的匿名构造函数。(见注释)

    注:根据C++类型退化规则,作为参数声明的函数等价于一个指向同类型函数的指针。参见C++函数对象的实例

C++标准中采取第二种解释,将这种MVP情况统一解释为函数,导致出错。

如何避免MVP

自C++11以后,首选方法是使用统一初始化:

// 以下均可
TimeKeeper time_keeper(Timer{});
TimeKeeper time_keeper{Timer()};
TimeKeeper time_keeper{Timer{}};
TimeKeeper time_keeper(     {}); // 甚至允许完全省略类型名称
TimeKeeper time_keeper{     {}};

在C++11之前,强制获得预期解释的常用手段是使用额外括号拷贝初始化,后一种写法更常见,而且可能被编译器优化,C++17开始,可以保证这种优化。

TimeKeeper time_keeper( (Timer()) ); // 额外括号
TimeKeeper time_keeper = TimeKeeper(Timer()); // 拷贝初始化

结语

总之结论就是,留意这种MVP,并在coding中避免MVP,对我自己来说以后除了写算法题还是老老实实在构造函数的初始化列表里面初始化吧。

参考资料:

  1. C++统一初始化.
  2. 最令人烦恼的解析MVP
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WuPeng_uin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值