基于范围的隐式添加“const”限定符?
Range based for implicitly adds `const` qualifier?
让我们看一下下面这个简单的基于范围的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
限定符,完全是无缘无故的。
- 为什么会这样?
- 是否有变通方法允许在基于 for 循环的范围内修改变量?
在
int a = 5, b = 6;
for (auto & i : {a, b})
你有 {a, b}
是两个元素 a
和 b
的 std::initialiser_list
,其中 a
和 [=16 的值=] 被复制。现在,std::initializer_list
only provides constant iterators to its elements,因为 initializer_list
是不可变的,所以你不能将值绑定到非 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
has an implicit conversion operator to T&
,如果在其他情况下您能够自动触发隐式转换(而不是调用 .get()
),我不会感到惊讶.
另请注意,{a, b}
不是从 a
到 b
的数字范围,它实际上只是这两个数字。所以使用 int a = 0, b = 10
你不会有 [0, 10]
但 0
的列表后跟 10
.
如果你想要 "proper" 个范围,我建议你看看 Boost.Range。
这与基于范围的循环无关。问题是 std::initializer_list<int>::iterator
是 const int*
。您不能更改 initializer_list
的内容。如果您改用 std::vector<int>
之类的类型,则可以正常工作。
让我们看一下下面这个简单的基于范围的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
限定符,完全是无缘无故的。
- 为什么会这样?
- 是否有变通方法允许在基于 for 循环的范围内修改变量?
在
int a = 5, b = 6;
for (auto & i : {a, b})
你有 {a, b}
是两个元素 a
和 b
的 std::initialiser_list
,其中 a
和 [=16 的值=] 被复制。现在,std::initializer_list
only provides constant iterators to its elements,因为 initializer_list
是不可变的,所以你不能将值绑定到非 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
has an implicit conversion operator to T&
,如果在其他情况下您能够自动触发隐式转换(而不是调用 .get()
),我不会感到惊讶.
另请注意,{a, b}
不是从 a
到 b
的数字范围,它实际上只是这两个数字。所以使用 int a = 0, b = 10
你不会有 [0, 10]
但 0
的列表后跟 10
.
如果你想要 "proper" 个范围,我建议你看看 Boost.Range。
这与基于范围的循环无关。问题是 std::initializer_list<int>::iterator
是 const int*
。您不能更改 initializer_list
的内容。如果您改用 std::vector<int>
之类的类型,则可以正常工作。