为什么这段代码有效
std::vector<int> intVector(10);
for(auto& i : intVector)
std::cout << i;
这不是吗?
std::vector<bool> boolVector(10);
for(auto& i : boolVector)
std::cout << i;
在后一种情况下,我收到错误
错误:从“std::_Bit_iterator::reference {aka std::_Bit_reference}”类型的右值对“std::_Bit_reference&”类型的非常量引用进行无效初始化
for(auto& i : boolVector)
std::vector<bool>
不是容器!
std::vector<T>
的迭代器通常会取消引用 T&
,您可以将其绑定到您自己的 auto&
。
然而,std::vector<bool>
将其bool
打包在整数内,因此您需要一个代理在访问它们时进行位屏蔽。因此,它的迭代器返回一个 Proxy
。Proxy
是纯右值(临时值),因此它无法绑定到左值引用,例如 auto&
。
解决方案:使用
auto&&
,如果给定一个左值引用,它将正确折叠为左值引用,或者如果给定代理,则绑定并保持临时活动。
std::vector<bool>
不遵守标准集装箱规则。
特别是,它的迭代器
operator*
不会返回 bool&
。
无效代码中的循环
#include <vector>
#include <iostream>
int main() {
std::vector<bool> boolVector(10);
for (auto& i: boolVector)
std::cout << i;
}
可以通过三种方式中的任何一种重写来迭代值:
(只读)
for (auto const i: boolVector)
std::cout << i;
(只读,可能效率低下)
for (auto const& i: boolVector)
std::cout << i;
(读/写²)
for (auto&& i: boolVector)
std::cout << i;
第一个和最后一个之间的选择取决于您是否需要修改向量中的值,或者只是读取它们。
备注:
我说“可能效率低下”,因为它有不必要的间接级别。任何像样的优化器都应该创建与第一个示例相同的代码。
for (auto i: boolVector)
还提供了向量上的 读/写视图(因为我们已经制作了 代理对象的副本)。但我不建议这样做,因为读者可能会期望此类写入仅具有局部效果,就像标准容器一样。
这就是为什么我将
auto const i
用于只读情况;或者,我们可以使用 boolVector | std::views::as_const
(C++23 起) 或使用向量的 const 引用 (for (auto const& v = boolVector; auto i: v)
) 来获得 const 代理。
vector<bool>
(通常)明确专门用于将每个bool
存储在单个位中,从而将存储成本从每个值一个字节减少到每八个值一个字节。我所知道的临时处理器中没有一个是位可寻址的,因此不可能存储对 vector<bool>
中的值的引用。您需要使用普通的 auto
,而不是 auto&
作为迭代值 i
。