基于范围的for隐式地添加了' const '限定符

Range based for implicitly adds `const` qualifier?

本文关键字:添加 const 于范围 范围 for      更新时间:2023-10-16

让我们看看下面基于for循环的简单范围:

  int a = 5, b = 6;
  for (auto & i : {a, b})
  {
      std::cout << i << std::endl; // Works as expected.
      i = 3;                       // Error!
  }

gcc抱怨assignment of read-only reference 'i',暗示与初始化列表一起使用的基于范围的for循环隐式地向引用添加了const限定符,完全没有理由。

  1. 为什么会这样?
  2. 是否有一种工作方式允许修改基于循环的范围内的变量?

In

int a = 5, b = 6;
for (auto & i : {a, b})

您知道{a, b}是两个元素abstd::initialiser_list,其中ab的值被复制。现在,std::initializer_list只提供常量迭代器到它的元素,因为initializer_list s是不可变的,所以你不能将值绑定到非const左值引用。

一种选择是传递指针,这将使指针本身成为常量,而不是它们所指向的值:

for (auto& i : {&a, &b}) 
    *i = 0;

现场演示

另一种选择是使用std::reference_wrapper,但在这种情况下仍然需要调用.get()或显式强制转换static_cast<int&>:

for (auto& i : {std::ref(a), std::ref(b)}) 
    i.get() = 0;

现场演示

考虑到std::reference_wrapper具有到T&的隐式转换操作符,如果在其他上下文中能够自动触发隐式转换(而不是调用.get()),我不会感到惊讶。


还要注意,{a, b}不是ab的数字范围,它实际上只是这两个数字。因此,对于int a = 0, b = 10,您将没有[0, 10],而是0后面跟着10的列表。

如果你想要一个"合适的"范围,我建议你看看Boost.Range.

这与基于范围的循环无关。问题是std::initializer_list<int>::iteratorconst int*。您不能更改initializer_list的内容。如果您要使用像std::vector<int>这样的类型,这就可以了。