基于范围隐式添加`const`限定符?

时间:2015-07-16 13:30:19

标签: c++ c++11 const initializer-list

让我们看一下基于循环的以下简单范围:

  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. 是否有解决方案允许修改基于循环的范围内的变量?

2 个答案:

答案 0 :(得分:22)

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

{a, b}std::initialiser_list两个元素ab,其中ab的值被复制。现在,std::initializer_list only provides constant iterators to its elements,因为initializer_list是不可变的,所以你不能将值绑定到非const左值引用。

一种选择是传递指针,这会使指针本身保持不变,但不会指向它们指向的值:

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

Live demo

另一个替代方案是使用std::reference_wrapper,但在这种情况下仍然需要调用.get()或显式广告static_cast<int&>

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

Live demo

考虑到std::reference_wrapper has an implicit conversion operator to T&,如果在其他情况下你能够自动触发隐式转换(而不是调用.get()),我不会感到惊讶。 / p>

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

如果你想拥有“适当”的范围,我建议你看一下Boost.Range

答案 1 :(得分:9)

这与基于范围的循环无关。问题是std::initializer_list<int>::iteratorconst int*。您无法更改initializer_list的内容。如果您使用类似std::vector<int>的类型,则可以正常使用。