阅读一些基于范围的循环的例子,他们提出了两种主要的方法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 
}

or

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时,它不是避免了每次迭代中的冗余复制吗?


当前回答

我会考虑

for (auto&& o : range_expr) { ...}

or

for (auto&& o : std::as_const(range_expr)) { ...}

它总是有效的。

并注意可能的临时范围表达式陷阱。

在c++ 20中

for (T thing = foo(); auto& x : thing.items()) { /* ... */ }

其他回答

我要在这里相反地说,在基于范围的for循环中不需要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的操作。在我看来,这很愚蠢,同样的论点也可以用于使用auto const &作为基于范围的for循环中的变量,而不仅仅是auto &。

当我们不需要更改vec项目时,示例建议使用第一版。

然后他们会给出错误的建议。

为什么他们不建议使用const引用呢

因为他们给出了错误的建议:-)你所说的是正确的。如果您只想观察一个对象,则不需要创建副本,也不需要对该对象使用非const引用。

编辑:

我看到你链接的引用都提供了在int值或其他一些基本数据类型的范围内迭代的例子。在这种情况下,由于复制int型的代价并不高,因此创建一个副本基本上等同于(如果不是比观察const &更有效的话)。

然而,对于用户定义的类型,通常不是这样。复制udt的代价可能很高,如果您没有创建副本的理由(例如在不改变原始对象的情况下修改检索到的对象),那么最好使用const &。

我会考虑

for (auto&& o : range_expr) { ...}

or

for (auto&& o : std::as_const(range_expr)) { ...}

它总是有效的。

并注意可能的临时范围表达式陷阱。

在c++ 20中

for (T thing = foo(); auto& x : thing.items()) { /* ... */ }

如果你有一个std::vector<int>或std::vector<double>,那么使用auto(带值复制)而不是const auto&是可以的,因为复制int型或double型比较便宜:

for (auto x : vec)
    ....

但是如果你有一个std::vector<MyClass>,其中MyClass有一些非简单的复制语义(例如std::string,一些复杂的自定义类等),那么我建议使用const auto&来避免深度复制:

for (const auto & x : vec)
    ....

如果你不想改变项目,也不想复制,那么auto const &是正确的选择:

for (auto const &x : vec)

任何建议你使用auto &的人都是错误的。忽略它们。

以下是概要:

当您想处理副本时,请选择auto x。 当您希望使用原始项目并可以修改它们时,请选择auto &x。 当您希望使用原始项并且不修改它们时,请选择auto const &x。