Range-for-loops 和 std::vector<bool>

问题描述 投票:0回答:3

为什么这段代码有效

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)
c++ c++11 for-loop range auto
3个回答
39
投票

因为

std::vector<bool>
不是容器

std::vector<T>
的迭代器通常会取消引用
T&
,您可以将其绑定到您自己的
auto&

然而,

std::vector<bool>
将其
bool
打包在整数内,因此您需要一个代理在访问它们时进行位屏蔽。因此,它的迭代器返回一个
Proxy

由于返回的
Proxy
是纯右值(临时值),因此它无法绑定到左值引用,例如
auto&

解决方案:使用

auto&&
,如果给定一个左值引用,它将正确折叠为左值引用,或者如果给定代理,则绑定并保持临时活动。


10
投票

std::vector<bool>
不遵守标准集装箱规则。

特别是,它的迭代器

operator*
不会返回
bool&

无效代码中的循环

#include <vector>
#include <iostream>

int main() {
  std::vector<bool> boolVector(10);
  for (auto& i: boolVector)
      std::cout << i;
}

可以通过三种方式中的任何一种重写来迭代值:

  1. (只读)

     for (auto const i: boolVector)
         std::cout << i;
    
  2. (只读,可能效率低下)

     for (auto const& i: boolVector)  
         std::cout << i;
    
  3. (读/写²)

     for (auto&& i: boolVector)
         std::cout << i;
    

第一个和最后一个之间的选择取决于您是否需要修改向量中的值,或者只是读取它们。


备注:

  1. 我说“可能效率低下”,因为它有不必要的间接级别。任何像样的优化器都应该创建与第一个示例相同的代码。

  2. for (auto i: boolVector)
    还提供了向量上的 读/写视图(因为我们已经制作了 代理对象的副本)。但我不建议这样做,因为读者可能会期望此类写入仅具有局部效果,就像标准容器一样。

    这就是为什么我将

    auto const i
    用于只读情况;或者,我们可以使用
    boolVector | std::views::as_const
    (C++23 起) 或使用向量的 const 引用 (
    for (auto const& v = boolVector; auto i: v)
    ) 来获得 const 代理。


6
投票

vector<bool>
(通常)明确专门用于将每个
bool
存储在单个位
中,从而将存储成本从每个值一个字节减少到每八个值一个字节。我所知道的临时处理器中没有一个是位可寻址的,因此不可能存储对
vector<bool>
中的值的引用。您需要使用普通的
auto
,而不是
auto&
作为迭代值
i

© www.soinside.com 2019 - 2024. All rights reserved.