267

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

4

5 回答 5

468

如果您不想更改项目并且想要避免复制,那么auto const &是正确的选择:

for (auto const &x : vec)

谁建议你使用auto &都是错误的。别理他们。

这是回顾:

  • 选择auto x何时要使用副本。
  • 选择auto &x何时要使用原始项目并可以修改它们。
  • 选择auto const &x何时要使用原始项目并且不会修改它们。
于 2013-03-02T15:27:29.477 回答
31

如果你有std::vector<int>or ,那么使用(with value copy) 而不是std::vector<double>就好了,因为复制 an or a很便宜:autoconst auto&intdouble

for (auto x : vec)
    ....

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

for (const auto & x : vec)
    ....
于 2013-03-02T15:33:07.820 回答
3

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

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

为什么他们不建议 const 引用的东西

因为他们给出了错误的建议:-)你提到的是正确的。如果您只想观察一个对象,则无需创建副本,也无需对其进行非const引用。

编辑:

我看到您链接的所有引用都提供了迭代一系列int值或其他一些基本数据类型的示例。在这种情况下,由于复制 anint并不昂贵,因此创建一个副本基本上等同于(如果不是更有效)拥有一个 observing const &

但是,对于用户定义的类型,情况通常并非如此。复制 UDT 的成本可能很高,如果您没有理由创建副本(例如修改检索到的对象而不更改原始对象),那么最好使用const &.

于 2013-03-02T15:28:12.487 回答
1

我将在这里相反,并说auto const &在基于范围的 for 循环中没有必要。告诉我你是否认为下面的函数很傻(不是它的目的,而是它的编写方式):

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;
}

在这里,作者创建了一个 const 引用,v用于所有不修改 v 的操作。在我看来,这很愚蠢,并且可以使用相同的参数auto const &作为基于范围的 for 循环中的变量,而不仅仅是auto &.

于 2013-03-02T16:03:12.223 回答
1

我会考虑

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

或者

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

它总是有效的。

并提防可能的临时范围表达式陷阱。

在 C++20 中,你有类似的东西

for (T thing = foo(); auto& x : thing.items()) { /* ... */ }
于 2020-10-31T11:48:11.480 回答