C++ 基于范围的循环:通过值或对常量的引用获取项?

C++ 基于范围的循环:通过值或对常量的引用获取项?,c++,c++11,C++,C++11,阅读一些基于范围的循环示例,他们提出了两种主要方法 嗯 当我们不需要更改vec项目时,IMO的示例建议使用第二个版本(按值)。为什么他们不建议const引用的内容(至少我没有找到任何直接的建议): for(auto-const&x:vec)/如果您不想更改项目,也不想复制,那么auto-const&是正确的选择: for (auto const &x : vec) 无论谁建议你使用auto&都是错误的。别理他们 以下是总结: 如果要处理副本,请选择“自动x” 当您想要使用原始项目并可

阅读一些基于范围的循环示例,他们提出了两种主要方法

当我们不需要更改
vec
项目时,IMO的示例建议使用第二个版本(按值)。为什么他们不建议
const
引用的内容(至少我没有找到任何直接的建议):


for(auto-const&x:vec)/如果您不想更改项目,也不想复制,那么
auto-const&
是正确的选择:

for (auto const &x : vec)
无论谁建议你使用
auto&
都是错误的。别理他们

以下是总结:

  • 如果要处理副本,请选择“自动x”
  • 当您想要使用原始项目并可以修改它们时,请选择
    auto&x
  • 如果要使用原始项目且不会修改它们,请选择“自动常量&x
” 当我们不需要更改
vec
项时,示例建议使用第一个版本

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

为什么他们不建议const参考的东西

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

编辑:

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


但是,对于用户定义的类型,通常情况下并非如此。UDT的复制成本可能很高,如果您没有创建副本的理由(例如在不更改原始对象的情况下修改检索到的对象),那么最好使用
const&
如果您有
std::vector
std::vector
,那么使用
auto
(带值复制)就可以了由于复制
int
double
很便宜,因此不使用
const auto&

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

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

在这里,我将相反地说,在基于范围的for循环中,不需要
auto const&
。如果您认为以下函数很愚蠢,请告诉我(不是因为它的用途,而是因为它的编写方式):

long-long-SafePop(标准::向量&v)
{
自动常数&cv=v;
长n=-1;
如果(!cv.empty())
{
n=cv.back();
v、 向后弹出();
}
返回n;
}
在这里,作者创建了一个对
v
的常量引用,用于所有不修改v的操作。在我看来,这是愚蠢的,同样的论点也适用于使用
auto const&
作为基于范围的for循环中的变量,而不是我会考虑的
auto&

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

它总是有效的

并谨防可能的陷阱

在C++20中,您可以使用

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

@BenjaminLindley:所以我可以推断你也会反对for循环中的
常量迭代器
?如何确保在对容器进行迭代时不会更改容器中的原始项?@BenjaminLindley:如果没有
const\u迭代器
,为什么代码无法编译?让我猜猜,您迭代的容器是
const
。但为什么一开始它是常量?在某些地方,您使用
const
来确保什么?创建对象
const
的好处就像在类中使用
私有
/
受保护
的好处一样。它避免了更多的错误。@BenjaminLindley:哦,我忽略了这一点。那么在这种情况下,实现也是愚蠢的。但是,如果该函数没有修改
v
,那么在我看来,实现就很好了。如果循环从未修改它所迭代的对象,那么在我看来,引用
const
似乎是正确的。不过我明白你的意思。我的观点是,循环表示的代码单元与函数非常相似,在该单元中没有需要修改值的指令。因此,您可以像使用
const
成员函数一样,将整个单元“标记”为
const
。因此,您认为基于范围的
const&
是愚蠢的,因为您编写了一个不相关的虚构示例,一个虚构的程序员在cv资格方面做了愚蠢的事情。。。好的,谢谢你的回答。我想还应该指出,
const auto&x
相当于您的第三个选择。@mloskot:相当于。(你说的“但它的语法相同”是什么意思?语法明显不同。)缺失:
auto&&
当你不想做不必要的复制,也不在乎是否修改它时,如果您只使用int/double这样的基本类型,那么引用的区别是否适用于副本?@racarate:我不能对总体速度进行评论,我认为没有人可以不首先分析它。坦率地说,我的选择不会基于速度,而是基于代码的清晰性。如果我想要不变性,我肯定会使用
const
。然而,无论是
auto-const&
,还是
auto-const
都没有什么区别。我会选择
auto const&
只是为了更加一致。
for (const auto & x : vec)
    ....
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;
}
for (auto&& o : range_expr) { ...}
for (auto&& o : std::as_const(range_expr)) { ...}
for (T thing = foo(); auto& x : thing.items()) { /* ... */ }