【问题标题】:C++11 Range-based for-loop efficiency "const auto &i" versus "auto i"C++11 基于范围的 for 循环效率“const auto &i”与“auto i”
【发布时间】:2013-06-06 14:29:30
【问题描述】:

在 C++11 中,我可以像这样迭代一些容器:

for(auto i : vec){
   std::cout << i << std::endl;
}

但我知道这是不必要的 - 不必要,因为我只需要打印 vec 的值 - 复制 (EDITvec 的每个元素,所以我可以这样做:

for(auto &i : vec){
   std::cout << i << std::endl;
}

但我想确保 vec 的值永远不会被修改并遵守 const-correctness,所以我可以这样做:

for(const auto &i : vec){
   std::cout << i << std::endl;
}

所以我的问题是:如果我只需要查看某个容器的值,那么最后一个循环 (const auto &amp;i) 是否总是首选,因为 not有一个额外的 (EDIT: each element of) vec?

我正在开发一个程序,我正在考虑在整个程序中进行此更改,因为效率在其中至关重要(我首先使用 C++ 的原因)。

【问题讨论】:

  • 是的,如果您只需要对某个参数进行读取访问,则应该通过auto const&amp; 来避免不必要的复制。
  • “const”关键字并没有让你的代码变得更快……
  • for (auto i : vec) 不会复制整个vec,而是将vec 的每个元素 复制到i
  • @Casey 我明白了。所以在每次新的迭代中,之前的副本都会被删除,对吧?
  • @user2052561 - 未删除,但已销毁。

标签: c++ for-loop c++11 auto


【解决方案1】:

是的。同样的原因,如果你只读过一个参数,你就将参数设为const&amp;

T        // I'm copying this
T&       // I'm modifying this
const T& // I'm reading this

这些是您的“默认值”。但是,当T 是基本类型(内置)时,您通常只需恢复到const T(无参考)即可阅读,因为副本比别名便宜。


我正在开发一个程序,我正在考虑在整个程序中进行此更改,因为效率在其中至关重要

  1. 不要盲目地彻底改变。一个工作程序比一个快速但损坏的程序要好。
  2. 你如何迭代你的循环可能不会有太大的不同;你循环是有原因的,不是吗?循环的主体很可能是罪魁祸首。
  3. 如果效率至关重要,您希望使用 profiler 来找出程序的哪些部分实际上很慢,而不是猜测可能的部分/em> 慢点。请参阅 #2,了解为什么您的猜测可能是错误的。

【讨论】:

  • “复制比别名便宜”仅适用于大多数基本类型,除了移动语义不能在 OP 的情况下使用并且因为它 T 可以是任何类型,引用将是正确的选择。
  • ""复制比别名更便宜"仅适用于大多数基本类型"这就是为什么该句子的前半部分致力于说它适用于基本类型... ?显然,如果您不知道类型,则使用一般情况。
  • 为什么编译器会在范围循环中生成不同的代码,例如“int”和“const int &”?将 args 传递给函数时会有所不同,但在范围循环中,编译器拥有优化它所需的一切。我尝试反汇编几个范围循环,生成的代码是一样的。对于具有平凡 dtor 和平凡 copy-ctor 的结构也是如此。
  • @SergioMartins:不会,这就是为什么你应该使用const T&amp;。一般来说,这是你想要的,当你碰巧知道T时,将其进一步修改为“const T”或只是“T”是没有意义的;编译器会为你做这件事。
  • const auto&amp; 表明您打算不修改任何内容并且您也不需要副本。即使性能相同,我也会这样做,因为它使您的代码更具表现力。
【解决方案2】:

免责声明:通常autoauto&amp; 之间的区别是微妙的,部分是风格问题,但有时也是正确性问题。我不打算在这里介绍一般情况!

在基于范围的for循环中,区别

for (auto element : container) {}

for (auto& element_ref : container) {}

elementcontainer中元素的副本,而element_ref是对容器中元素的引用。

要查看操作上的差异,请考虑以下示例:

#include <iostream>

int main(void) {
    int a[5] = { 23,443,16,49,66 };

    for (auto i : a) i = 5;       
    for (const auto& i : a) std::cout << i << std::endl;
    for (auto& i : a) i = 5;   
    for (const auto& i : a) std::cout << i << std::endl;    
}

它会打印出来

23
443
16
49
66
5
5
5
5
5

因为第一个循环作用于数组元素的副本,而第二个实际上修改了数组中的元素。

如果您不想修改元素,那么const auto&amp; 通常更合适,因为它避免了复制元素(这可能很昂贵)。

【讨论】:

    【解决方案3】:

    想象一下,如果你的向量包含字符串。长弦。 5000 长串。不必要地复制它们,你最终会得到一个写得很好的 for 循环,但效率非常低。

    确保您的代码符合您的意图。如果您不需要循环内的副本,请不要复制。

    使用上面建议的引用 & 或迭代器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-23
      • 1970-01-01
      • 2015-01-15
      • 2021-04-08
      • 2011-02-20
      相关资源
      最近更新 更多