0

用 ranged for (用于读取元素值)迭代原始类型容器的更好方法(性能)是什么 - 按 ref 或按值读取元素?

std::vector<int> v;
for (const auto e : v) { std::cout << e; }

或者

for (const auto& e : v) { std::cout << e; }

有: 通过引用传递原始类型会适得其反吗?

想知道这 2 件事(通过 ref 和 value 传递和迭代)是否可能以某种方式相关。

另一个注意事项:我确实认识到通过 ref、const-ref 和 value 访问以及复制值之间有什么区别 - 我只对只读方式的性能更好感兴趣。

4

3 回答 3

2

对于小型的原始类型,如果您不想修改容器的元素,那么这确实是样式问题,尽管对于这种“只读”访问,使用按值方法可能更安全(这实际上会阻止对“原件”的任何意外修改——尽管const引用上的限定符也会阻止这种情况)。

但是,如果您确实想修改包含的元素,则需要使用引用变量进行迭代,如以下代码示例所示:

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v{ 1,2,3,4,5 };

    for (auto e : v) ++e;                   // By value: takes copies
    for (auto f : v) std::cout << f << " "; // Elements unchanged
    std::cout << std::endl;

    for (auto& e : v) ++e;                  // By reference: refers to actual elements
    for (auto f : v) std::cout << f << " "; // Elements incremented
    std::cout << std::endl;

//  for (const auto& e : v) ++e;            // const reference: Compiler error!

    return 0;
}
于 2020-12-02T09:04:48.217 回答
1

我并不是说这是唯一正确的方法,但它是一种完全有效的方法:编写代码时不要担心性能。关心可读性并在您有正确且有效的内容时保持性能。

以此为前提,我的建议如下:

当您不需要修改容器元素时,将其用作默认值:

for (const auto& e : v) {}

仅当由于某种原因您需要元素的副本(但仍不想修改元素)时才使用

void foo(int& x) {}
for (auto e : v) {
    e += 5;
    foo(e);
}

如果您需要修改元素,请使用参考:

 for (auto& e : v) {}

最后但并非最不重要的一点是,一旦您编写了正确的代码,并且通过测量您意识到这个循环是一个瓶颈:查看程序集以了解什么更好。

但是,考虑到任何重要的循环体都可能超过复制小类型和获取引用之间的差异。

于 2020-12-02T09:09:10.713 回答
1

理论上,对象变量可能更有效,因为它不涉及通过引用进行间接寻址。然而,至少在这个简单的示例中,参考版本可以优化为与对象版本相同,因此在实践中不一定重要。

想知道这 2 件事(通过 ref 和 value 传递和迭代)是否可能以某种方式相关。

是的。相同的经验法则适用于这两种情况。在这两种情况下,优化器是否内联所有内容都可能无关紧要。

于 2020-12-02T10:02:39.507 回答