vector 指针_C++ vector<bool> 的巨坑与range_based_for

本来开公众号是想分享心情的~但是没想到本公众号第一篇有点东西的文章竟然是关于C++的233。那,就这样吧。

最近写代码的时候写了下面这样的代码:

06746891952a0c80fc96b436ad2540e6.png

神奇的是,这段看似平常的代码居然在g++上编译错误。好奇的我换了MSVC结果仍然编译错误。但是如果把bool换成int,那么编译就会非常正确:

ee8f32eaaff4490373d3be370b6f18ef.png

这两段看似一样的代码,却有两种不同的编译结果。这真是让人摸不着头脑。这个时候,我们就要拿出我们的利器——百度来解决这个问题。

搜索C++ reference,在里面我们可以看到vector原来是vector模板类的一个偏特化(specialization),而并不是由vector直接实例化得到的。C++ reference给我们的解释是,为了便于把bool优化成比特存储,而不需要一个字节来存储。那为什么上面的二重range_based_for会失败呢?这时我们查阅一下C++标准中vector 是怎样定义的(略去了不必要的代码):

8542d645eb08456a49667a9864fda989.png

结果非常的amazing啊!我们对vector的每个元素进行访问的时候,实际上访问的都是vector::reference这个类,而不是其成员本身。毕竟如果编译器想要将其每个元素优化成二进制位的话,C++语言由于不存在直接访问二进制位的操作,就无法直接访问其每个元素,所以统一为访问这个类来对每个元素进行引用。了解了这些,我们想解答编译出错的事情,还需要知道range_based_for的实现。C++标准中如此规定range_based_for:

ec6457c6ec123f3f135a5f158ecf3057.png

那么,我们对一维情况vector v; for (auto& velem : v) {}进行等价展开,它就等价于:

0966c0edd13848eaeb7656f55ecf023f.png

显然标记了//error!的那一行就产生了错误!因为__begin.operator*()是一个函数调用,而函数的返回值是一个右值(rvalue),即vector::reference的临时对象,而我们却给它赋值给了左值引用vector::reference&,因此会报出编译错误。

那么,我们如果要修改v的元素该怎么办呢?这是一个令人发指的操作:注意到我们的vector::reference是对vector的一个元素的引用(可以理解为指针),那么我们只需要值传递vector::reference即可,不需要进行引用传递!即代码如下:

2f388816ec04f764410c74f4ba4a7eb2.png

输出的结果果然是1!这个操作与其他类型的vector完全不同。因为我们知道,把vector换成vector的话:

2effd394a482deac6c32b5431357f1de.png

它的输出结果显然仍然是0,因为值传递不1能改变v内元素的值,而vector与我们的习惯完全不同!这是需要特别注意的。

我们回到最开始的问题,我起初报错的代码就可以改成:

bb239f481d71642cdea9563caaabcf9a.png

这就是vector与普通的vector的不同之处。因此,C++ reference中也警告我们,我们无法使用无特化的bool类型。如果这种特化的bool类型不能满足我们的需求,我们只能用charunsigned char或其他的封装类型来代替,或者使用其他的容器如deque来代替。

参考文献

1.C++ reference:  

http://www.cplusplus.com/reference/vector/vector-bool/ 

2.《ISO/IEC 14882: 2017 Programming languages — C++》

文章最后要吐槽一句,这个秀米的代码块是个什么东西啊……乱糟糟的。只好换成图片了233

https://blog.csdn.net/Timothy6/article/details/110688913

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值