阅读基于范围的循环的一些示例,他们提出了两种主要方式 1,2,3,4
std::vector<MyClass> vec;
for (auto &x : vec)
{
// x is a reference to an item of vec
// We can change vec's items by changing x
}
或
for (auto x : vec)
{
// Value of x is copied from an item of vec
// We can not change vec's items by changing x
}
好。
当我们不需要更改vec
项时,IMO,示例建议使用第二个版本(按值)。为什么他们不提出const
引用的内容(至少我没有找到任何直接的建议):
for (auto const &x : vec) // <-- see const keyword
{
// x is a reference to an const item of vec
// We can not change vec's items by changing x
}
不是更好吗?当它是const
时,它是否避免了每次迭代中的冗余副本?
答案 0 :(得分:325)
如果您不想更改项目以及避免制作副本,那么auto const &
是正确的选择:
for (auto const &x : vec)
建议您使用auto &
的人是错误的。忽略它们。
这是概述:
auto x
。auto &x
,然后修改它们。auto const &x
,不会修改它们。答案 1 :(得分:21)
如果您有std::vector<int>
或std::vector<double>
,则可以使用auto
(带值复制)而不是const auto&
,因为复制{{1}或者int
便宜:
double
但是如果你有一个for (auto x : vec)
....
,其中std::vector<MyClass>
有一些非平凡的复制语义(例如MyClass
,一些复杂的自定义类等),那么我建议使用< strong> std::string
以避免深层拷贝:
const auto&
答案 2 :(得分:3)
如果我们不需要更改
vec
项,则示例建议使用第一个版本。
然后他们给出了错误的建议。
为什么他们不建议const引用的东西
因为他们给出了错误的建议:-)你提到的是正确的。如果您只想观察某个对象,则无需创建副本,也无需对其进行非const
引用。
修改强>
我看到您链接的引用都提供了迭代一系列int
值或其他基本数据类型的示例。在这种情况下,由于复制int
并不昂贵,因此创建副本基本上等同于(如果不是更有效)观察const &
。
但是,对于用户定义的类型,通常情况并非如此。复制UDT可能很昂贵,如果您没有理由创建副本(例如修改检索到的对象而不更改原始副本),则最好使用const &
。
答案 3 :(得分:1)
我在这里会反对,并说在基于循环的范围内不需要auto const &
。如果您认为以下函数是愚蠢的(不是出于其目的,而是出现在它的编写方式),请告诉我:
long long SafePop(std::vector<uint32_t>& v)
{
auto const& cv = v;
long long n = -1;
if (!cv.empty())
{
n = cv.back();
v.pop_back();
}
return n;
}
在这里,作者创建了一个对v
的const引用,用于所有不修改v的操作。在我看来,这很愚蠢,并且使用{{1}可以做出同样的论证}作为基于循环的范围中的变量,而不仅仅是auto const &
。
答案 4 :(得分:0)
我会考虑的
for(auto && o:range_expr){...}
或
for(auto && o:std :: as_const(range_expr)){...}
它始终有效。.
并当心可能的Temporary range expression陷阱。
在C ++ 20中,您会遇到类似for (T thing = foo(); auto& x : thing.items()) { /* ... */ }