问题描述
在增强 for 循环中,遍历 vector<int>
和 vector<bool>
,并通过引用修改数组中的每个元素:
遍历 int
的代码不会报错:
std::vector<std::vector<int>> arr{{1, 0, 0}, {0, 0, 0}};
for (auto & rows : arr) {
for (int & tmp : rows) {
tmp = 0;
}
}
然而,如果将 int
替换为 bool
:
std::vector<std::vector<bool>> arr{{1, 0, 0}, {0, 0, 0}};
for (auto & rows : arr) {
for (bool & tmp : rows) { // 报错:类型不必配!bool & tmp 为左值引用,rows 返回的类型为 std::__bit_iterator<std::vector<bool>>
tmp = 0;
}
}
报错信息
=> 则会报错:Non-const lvalue reference to type 'bool' cannot bind to a temporary of type 'std::__bit_iterator<std::vector<bool>, false, 0>::reference' (aka 'std::__bit_reference<std::vector<bool>, true>')
=> 即使将非常量引用,改为常量引用,也会有警告:Clang-Tidy: The type of the loop variable 'tmp' is different from the one returned by the iterator and generates an implicit conversion; you can either change the type to the matching one ('const std::__bit_iterator<std::vector<bool>, false, 0>::reference &' (aka 'const std::__bit_reference<std::vector<bool>, true> &') but 'const auto&' is always a valid option) or remove the reference to make it explicit that you are creating a new value
分析
上述报错表明语句在将一个临时对象绑定到一个非常(const)值的左值引用上,导致了类型不匹配。具体地,临时对象为 rows
,类型为 vector<bool> &
,而 tmp
则为 bool
类型的非常量左值引用。问题来了:为什么 vector<int> &
绑定到 int
类型的非常量左值引用可以,换做 bool
就报错了呢?
仔细看报错信息,临时对象的类型为 std::__bit_iterator<std::vector<bool>>
,即 vector<bool>::iterator
返回的不是真正的 bool
,而是 std::__bit_iterator<std::vector<bool>>
!
文档记载
实际上,en.cppreference.com
中对这点有记录:
bool
类型本身的大小随实现而定,但很多编译器中都是一个字节:

然而,vector<bool>
从节省空间的角度(同时如果实现恰当的话还会加速执行时间),将数组中的每个元素实现成 1bit,而不是一个 sizeof(bool) bytes。

其他
对于这一点,其他地方也都有相关的描述,有的将这种实现视为一种双刃剑,有的认为这是一种错误:
-
paper “vector: More Problems, Better Solutions” http://www.gotw.ca/publications/N1211.pdf
-
StackOverflow https://stackoverflow.com/questions/8399417/why-vectorboolreference-doesnt-return-reference-to-bool
-
cplusplus https://cplusplus.com/reference/vector/vector-bool/
-
Specialization for bool [Wiki] https://en.wikipedia.org/w/index.php?title=Sequence_container_(C%2B%2B)&oldid=767869909#Specialization_for_bool
The Standard Library defines a specialization of the
vector
template forbool
. The description of this specialization indicates that the implementation should pack the elements so that everybool
only uses one bit of memory. This is widely considered a mistake.vector<bool>
does not meet the requirements for a C++ Standard Library container. For instance, acontainer<T>::reference
must be a true lvalue of typeT
. This is not the case withvector<bool>::reference
, which is a proxy class convertible tobool
. Similarly, thevector<bool>::iterator
does not yield abool&
when dereferenced. There is a general consensus among the C++ Standard Committee and the Library Working Group thatvector<bool>
should be deprecated and subsequently removed from the standard library, while the functionality will be reintroduced under a different name. -
boost 同时给出了替代的方法:
So
boost::container::vector<bool>::iterator
returns realbool
references and works as a fully compliant container. If you need a memory optimized version ofboost::container::vector<bool>
functionalities, please use Boost.DynamicBitset.
解决方案
- 使用
std::vector<char>
- 使用
std::bitset
-> https://en.cppreference.com/w/cpp/utility/bitset - 下载 boost 库
boost::container::vector<bool>
-> https://www.boost.org/doc/libs/1_48_0/doc/html/container/Cpp11_conformance.html#container.Cpp11_conformance.Vector_bool