vector<T>标准库模版类应该是绝大多数c++程序员使用频率比较高的一个类了。不过vector<bool>也许就不那么被程序员所了解。关于vector<bool>不尝试研究一番,一般还不太容易知道其中蕴含的问题。
首先得明确一点,那就是vector<bool>是vector<T>的特化版。这个特化版本要解决的问题就是存储容量的问题。
To optimize space allocation, a specialization of vector for bool elements is provided.
所以,它一般来说是以位的方式来存储bool的值。从这里我们可以看出,如果使用位来提升空间效率可能引出的问题就是时间效率了。因为俺们的计算机地址是以字节为单位的。这里可以看一个例子:
int main() { clock_t t1, t2, t3; vector<bool> vb; vector<int> vi; vector<char> vc; t1 = clock(); for (int i = 0; i < 1000; ++i) { for (int j = 0; j < 1000; ++j) { vb.push_back(true); } } t1 = clock() - t1; t2 = clock(); for (int i = 0; i < 1000; ++i) { for (int j = 0; j < 1000; ++j) { vi.push_back(1); } } t2 = clock() - t2; t3 = clock(); for (int i = 0; i < 1000; ++i) { for (int j = 0; j < 1000; ++j) { vc.push_back('a'); } } t3 = clock() - t3; cout << "'vector<bool>::push_back(true)' 1000000 times cost: " << t1 << endl; cout << "'vector<int>::push_back(1)' 1000000 times cost: " << t2 << endl; cout << "'vector<char>::push_back('a')' 1000000 times cost: " << t3 << endl; t1 = clock(); for (int i = 0; i < 1000; ++i) { for (int j = 0; j < 1000; ++j) { bool b = vb[j + 1000 * i]; } } t1 = clock() - t1; t2 = clock(); for (int i = 0; i < 1000; ++i) { for (int j = 0; j < 1000; ++j) { int b = vi[j + 1000 * i]; } } t2 = clock() - t2; t3 = clock(); for (int i = 0; i < 1000; ++i) { for (int j = 0; j < 1000; ++j) { char b = vc[j + 1000 * i]; } } t3 = clock() - t3; cout << "'vector<bool>::operator[]' 1000000 times cost: " << t1 << endl; cout << "'vector<int>::operator[]' 1000000 times cost: " << t2 << endl; cout << "'vector<char>::operator[]' 1000000 times cost: " << t3 << endl; return 0; }
我的机器上的输出结果是:
vector<bool>的耗时要比vector<T>多至少40倍以上!不简单。。。
这里我们只是简单得验证了下vector<bool>的特别之处。接下来再从标准文档中看看有什么特别之处:
There is no requirement that the data be stored as a contiguous allocation of bool values. A space-optimized representation of bits is recommended instead.
所以,vector<bool>就没要求说底层的存储必须是连续的空间。这也就从一个方面说明了我们上面的例子有如此之大的差异一个原因。
另外,事实上vetor<bool>只是从暴露出来的API上看,是一个容器。而事实上他丫的就不是个容器。
这还得先简单说说C++的容器。我们知道vector<T>(T不是bool)是一个顺序容器(Sequence container)。因此他满足C++标准中关于容器的标准。比方说下面这条:
Expression | Return type |
X::reference | lvalue of T |
简单说vector<int>::reference至少必须是一个int。而vector<bool>则不是这样的。vector<bool>::reference是一个可以和bool兼容的代理数据结构。
最简单的验证这一点的例子就是:
template <typename T> void func(vector<T>& v) { T& ref = v[0]; }
上面这段代码你在vc2012或者g++4.6.1上,传递一个vector<bool>对象,都不能通过编译。g++给出的编译错误就相当清晰了:
vectorbool.cpp: In function ‘void func(std::vector<T>&) [with T = bool']’:
vectorbool.cpp:18:11: instantiated from here
vectorbool.cpp:9:17: error: invalid initialization of non-const reference of type ’bool&’ from an rvalue of type ‘std::vector<bool>::reference {aka std::_Bit_reference}’
operator[]返回的reference是一个右值就不是个左值(lvalue of T)。
所以,vector<bool>就不是个容器。
总结一下:
- vector<bool>可能存在性能问题
- 它并不是个真正的容器,因此在模版代码上的使用可能会有问题。如果问题能发生在编译器那还好,换到运行期,还真有点麻烦(vector<bool>::operator[] misbehavior?)。
Reference:
- What You Should Know about vector<bool>
- The Fate of vector<bool> in C++09
- vector<bool>: More Problems, Better Solutions
更新(2017年3月27日)
vector<bool>因为其特殊性,所以在使用Range-based for loop时,要小心。比方说:
1 int main() 2 { 3 std::vector<bool> bools { false, false, false } 4 5 // change value (expected to see bools are not changed). 6 for (auto b : bools) 7 { 8 b = true; 9 } 10 11 // print bools 12 for (auto const & b : bools) 13 { 14 std::cout << std::boolalpha << b << std::endl; 15 } 16 17 return 0; 18 }
我们会发现bools里的value全部变成true了。真是头大。