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

时间:2013-03-02 15:25:07

标签: c++ c++11

阅读基于范围的循环的一些示例,他们提出了两种主要方式 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时,它是否避免了每次迭代中的冗余副本?

5 个答案:

答案 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()) { /* ... */ }