基于范围的循环:按值获取项目或引用 const?
Posted
技术标签:
【中文标题】基于范围的循环:按值获取项目或引用 const?【英文标题】:Range based loop: get item by value or reference to const? 【发布时间】:2013-02-17 00:55:38 【问题描述】:阅读一些基于范围的循环示例,他们提出了两种主要方法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
时,它不是避免了每次迭代中的冗余副本吗?
【问题讨论】:
【参考方案1】:如果您不想更改项目并且想避免复制,那么auto const &
是正确的选择:
for (auto const &x : vec)
建议您使用auto &
的人是错误的。忽略它们。
这里是回顾:
如果您想处理副本,请选择auto x
。
如果您想使用原始项目并可以修改它们,请选择 auto &x
。
如果您想使用原始项目并且不修改它们,请选择 auto const &x
。
【讨论】:
感谢您的出色回答。我想还应该指出const auto &x
相当于你的第三选择。
@mloskot:它是等价的。 (“但语法相同”是什么意思?语法明显不同。)
缺少:auto&&
,当您不想制作不必要的副本,不在乎是否修改它,只想工作。
如果您只使用 int/double 等基本类型,引用区别是否适用于副本?
@racarate:我无法评论整体的速度,而且我认为没有人可以评论它,除非先对其进行分析。坦率地说,我不会将我的选择基于速度,而是代码的清晰度。如果我想要不变性,我肯定会使用const
。不过,是auto const &
,还是auto const
,差别不大。我会选择 auto const &
只是为了更加一致。【参考方案2】:
当我们不需要更改
vec
项目时,示例建议使用第一个版本。
然后他们给出了错误的建议。
为什么他们不建议 const 引用的东西
因为他们给出了错误的建议 :-) 你提到的是正确的。如果您只想观察一个对象,则无需创建副本,也无需对它进行非const
引用。
编辑:
我看到您链接的所有参考资料都提供了迭代一系列 int
值或其他一些基本数据类型的示例。在这种情况下,由于复制 int
并不昂贵,因此创建副本基本上等同于(如果不是更有效)观察 const &
。
但是,一般情况下,用户定义的类型并非如此。复制 UDT 的成本可能很高,如果您没有理由创建副本(例如修改检索到的对象而不更改原始对象),那么最好使用 const &
。
【讨论】:
【参考方案3】:如果您有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)
....
【讨论】:
【参考方案4】:我将在这里相反,并说在基于范围的 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 &
。
【讨论】:
@BenjaminLindley:所以我可以推断您也会在 for 循环中反对const_iterator
?如何确保在迭代容器时不会更改容器中的原始项目?
@BenjaminLindley:如果没有const_iterator
,为什么您的代码无法编译?让我猜猜,你迭代的容器是const
。但是为什么是const
开头呢?您在某处使用const
来确保什么?
创建对象const
的优势就像在类中使用private
/protected
的优势一样。它避免了进一步的错误。
@BenjaminLindley:哦,我忽略了这一点。那么在这种情况下,实现也是愚蠢的。但是如果那个函数没有修改v
,IMO 的实现会很好。如果循环从不修改它所遍历的对象,那么在我看来,对const
有一个引用是正确的。不过我明白你的意思。我的是循环代表一个代码单元,就像函数一样,在那个单元内没有需要修改值的指令。因此,您可以将整个单元“标记”为 const
,就像使用 const
成员函数一样。
所以你认为基于范围的const &
是愚蠢的,因为你写了一个不相关的虚构例子,一个虚构的程序员用 cv-qualification 做了一些愚蠢的事情? ...好的,那么【参考方案5】:
我会考虑
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()) /* ... */
【讨论】:
你能补充一下为什么更喜欢auto&&
而不是auto&
吗?
@LyndenShields auto&&
可以绑定到所有内容。 auto&
不会绑定到右值,如果range_expression
返回右值,则不会工作。
有range_expression
返回右值的合理/现实例子吗?
好问题。我还没有任何例子。以上是关于基于范围的循环:按值获取项目或引用 const?的主要内容,如果未能解决你的问题,请参考以下文章