本来开公众号是想分享心情的~但是没想到本公众号第一篇有点东西的文章竟然是关于C++的233。那,就这样吧。
最近写代码的时候写了下面这样的代码:
神奇的是,这段看似平常的代码居然在g++上编译错误。好奇的我换了MSVC结果仍然编译错误。但是如果把bool
换成int
,那么编译就会非常正确:
这两段看似一样的代码,却有两种不同的编译结果。这真是让人摸不着头脑。这个时候,我们就要拿出我们的利器——百度来解决这个问题。
搜索C++ reference,在里面我们可以看到vector
原来是vector
模板类的一个偏特化(specialization),而并不是由vector
直接实例化得到的。C++ reference给我们的解释是,为了便于把bool
优化成比特存储,而不需要一个字节来存储。那为什么上面的二重range_based_for会失败呢?这时我们查阅一下C++标准中vector
是怎样定义的(略去了不必要的代码):
结果非常的amazing啊!我们对vector
的每个元素进行访问的时候,实际上访问的都是vector::reference
这个类,而不是其成员本身。毕竟如果编译器想要将其每个元素优化成二进制位的话,C++语言由于不存在直接访问二进制位的操作,就无法直接访问其每个元素,所以统一为访问这个类来对每个元素进行引用。了解了这些,我们想解答编译出错的事情,还需要知道range_based_for的实现。C++标准中如此规定range_based_for:
那么,我们对一维情况vector v; for (auto& velem : v) {}
进行等价展开,它就等价于:
显然标记了//error!
的那一行就产生了错误!因为__begin.operator*()
是一个函数调用,而函数的返回值是一个右值(rvalue),即vector::reference
的临时对象,而我们却给它赋值给了左值引用vector::reference&
,因此会报出编译错误。
那么,我们如果要修改v
的元素该怎么办呢?这是一个令人发指的操作:注意到我们的vector::reference
是对vector
的一个元素的引用(可以理解为指针),那么我们只需要值传递vector::reference
即可,不需要进行引用传递!即代码如下:
输出的结果果然是1!这个操作与其他类型的vector
完全不同。因为我们知道,把vector
换成vector
的话:
它的输出结果显然仍然是0,因为值传递不1能改变v
内元素的值,而vector
与我们的习惯完全不同!这是需要特别注意的。
我们回到最开始的问题,我起初报错的代码就可以改成:
这就是vector
与普通的vector
的不同之处。因此,C++ reference中也警告我们,我们无法使用无特化的bool
类型。如果这种特化的bool
类型不能满足我们的需求,我们只能用char
、unsigned 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