C ++ 11基于范围的循环:通过值或引用const获取项目


215

读取基于范围环的一些例子他们建议两种主要方式1234

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吗?

Answers:


390

如果您不想更改的项目,以及要避免复印,然后auto const &是正确的选择:

for (auto const &x : vec)

任何建议您使用的人auto &都是错误的。别管他们。

总结:

  • 选择auto x您想使用副本的时间。
  • 选择auto &x何时要使用原始项目并可以对其进行修改。
  • 选择auto const &x何时要使用原始项目,而不修改它们。

21
感谢您的出色回答。我想也应该指出,这const auto &x等同于您的第三选择。
smg 2015年

7
@mloskot:这是等效的。(以及“它是相同的语法”是什么意思?该语法明显不同。)
Nawaz

14
丢失:auto&&当您不想制作不必要的副本,并且不在乎是否修改它,而只是想工作时。
Yakk-亚当·内夫罗蒙特2015年

4
如果您仅使用int / double之类的基本类型,引用区别是否适用于副本?

4
@racarate:我无法对整体速度发表评论,而且我认为没有人可以对它进行概要分析。坦白说,我不会基于速度,而取决于代码的清晰度。如果我想要不变性,那我const肯定会用。但是,无论是auto const &还是auto const 几乎没有区别。我选择auto const &只是为了更加一致。
Nawaz

23

如果您有一个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)
    ....

3

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

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

为什么他们不建议const引用的内容

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

编辑:

我看到所有链接的参考都提供了在一系列int值或其他一些基本数据类型上进行迭代的示例。在那种情况下,由于复制an int并不昂贵,因此创建一个副本基本上等效于(如果效率更高的话)进行观察const &

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


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 &基于循环的范围中的变量使用相同的参数,而不仅仅是auto &


3
@BenjaminLindley:所以我可以推断出您也会const_iterator在for循环中提出反对意见?在迭代容器时,如何确保不更改容器中的原始项目?
Nawaz

2
@BenjaminLindley:为什么没有您的代码就不能编译const_iterator?让我猜测一下,您迭代器所在的容器是const。但是为什么const要开始呢?您正在使用某个地方const来确保什么?
Nawaz

8
制作对象const的优点就像在类中使用private/ 的优点一样protected。它避免了进一步的错误。
masoud

2
@BenjaminLindley:哦,我忽略了这一点。那么在这种情况下,实现也是愚蠢的。但是,如果该功能未修改v,则可以执行IMO。而且,如果循环从不修改迭代的对象,对我来说,引用似乎是正确的const。我明白你的意思了。我的想法是,循环代表的代码单元非常类似于函数,并且在该单元内没有指令需要修改值。因此,您可以const像对const成员函数那样将整个单元“标记”为。
Andy Prowl

1
所以您认为const &基于范围的方法是愚蠢的,因为您写了一个虚构的程序员的虚构示例,该程序员对cv -qualification 做过愚蠢的事情?...好吧
underscore_d
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.