【问题标题】:Incrementing iterators: Is ++it more efficient than it++? [duplicate]递增迭代器:++it 比 it++ 更高效吗? [复制]
【发布时间】:2010-11-07 19:14:00
【问题描述】:

可能重复:
Is there a performance difference between i++ and ++i in C++?

我正在编写一个程序,其中使用迭代器循环 std::vector。有人告诉我,在 for 语句中执行 ++it 会导致代码更高效。换句话说,他们是在说:

for ( vector<string>::iterator it=my_vector.begin(); it != my_vector.end(); ++it )

跑得比

快
for ( vector<string>::iterator it=my_vector.begin(); it != my_vector.end(); it++ )

这是真的吗?如果是,效率提升背后的原因是什么?它++/++所做的只是将迭代器移动到向量中的下一项,不是吗?

【问题讨论】:

  • 一模一样的副本,但代表竞赛仍在进行中。
  • 这是关于迭代器的,答案也应该如此。其他问题似乎在询问整体差异。我认为它们是相关的问题,但不是完全重复的问题?
  • @litb: 艰难的呼叫... 另一个问题是询问两个运算符之间特定于 C++ 的性能差异,答案反映了这一点,讨论了对象实现如何可能导致这种差异。迭代器是(或可以是)对象;所以这实际上是上一个问题的一个子集。
  • 这就是说,我同意你的观点,当他只问效率时,似乎没有区别。看起来像个骗子! :)

标签: c++ stl iterator


【解决方案1】:

前置增量更快的原因是后置增量必须复制旧值才能返回。正如GotW #2 所说,“前增量比后增量更有效,因为对于后增量,对象必须自增,然后返回一个包含其旧值的临时值。请注意,即使对于像 int 这样的内置函数也是如此。”

GotW #55提供了postincrement的规范形式,这表明它必须做preincrement加上更多的工作:

T T::operator++(int)
{
  T old( *this ); // remember our original value
  ++*this;        // always implement postincrement
                  //  in terms of preincrement
  return old;     // return our original value
}

正如其他人所指出的,某些编译器可能会在某些情况下对此进行优化,但如果您不使用返回值,最好不要依赖此优化。此外,对于具有微不足道的复制构造函数的类型,性能差异可能非常小,尽管我认为在 C++ 中使用预增量是一个好习惯。

【讨论】:

  • 请注意,如果旧值的副本永远不会被使用,体面的编译器会优化掉它。
  • 确切地说:你不能保证前增量比后增量更有效,因为 (a) 标准不需要它,并且 (b) 很容易发明源代码和编译器的组合,其中两者效率相同。
  • 但是你可以假设前增量至少和后增量一样有效。那么,当它们在其他方面等价时,为什么不总是使用它呢?
  • 同意:我认为在可能的情况下使用预增量,因为我想不出比发布更糟糕的正当理由。如果某些类重载了两个运算符(严重)并且以某种方式使预增量变得更糟,则不能因为未能解决它而责备调用者。我只是不认为人们应该承诺它“更有效”(许多人会认为它“严格来说更有效”),当没有这样的保证时,实际上它们在优化代码中通常是相同的。
【解决方案2】:

这不太可能对矢量产生任何影响。

一般来说,++it 极不可能比it++ 慢(假设一个合理的实现,如果它们超载的话),而且可能会更快。原因是如果迭代器类本身很复杂,那么由于it++ 必须在it 递增之前返回值,因此实现通常会进行复制。

向量迭代器可能“只是指针”(在优化的非调试版本中),operator++s 都将被内联。由于返回值未使用,因此通常会省略副本。所以它不会有任何区别。我有输入++it 的习惯,因为:

1) 对于某些迭代器类型,有一天它可能会有所不同,我不想为该类型做一些特殊的事情。

2)我个人认为前缀运算符更清楚地表达了意图:“增加它”,而不是“使用它然后增加”。

【讨论】:

  • 我不得不说,对我来说,看到 ++it 比看到它 ++ 更令人困惑。例如,查看常规的 int for 循环,for (int i = 0; i
  • @Josh:好吧,但这只是新手完全混淆 for 循环的最后一部分何时执行的结果。当然,你仍然初始化为 0,它在第一次循环时仍然是 0,第二次是 1,依此类推,不管你是写 ++i 还是 i++。坦率地说,如果写++i 提示新来者尽早解决这种困惑,并理解该子句在循环体之后执行,无论其中有什么,那么我会评价这是++i的额外优势!
  • 我想在我上大学之前,我应该归咎于高中的糟糕老师。他们告诉我们 ++i 意味着它会在评估循环内的内容之前递增,而 i++ 会在评估循环内的内容后递增。如果我从一开始就被正确地告知,那么混乱就不会存在。
【解决方案3】:

it++ 执行以下操作:

  1. 创建it 的副本
  2. 递增it
  3. 返回原始(非递增)it

++it 执行以下操作:

  1. 递增it
  2. 返回it

因为it++创建了一个副本,所以可以说是“比较慢”。但是,任何体面的编译器都会针对大多数定义的类型优化这种差异。对于某些用户定义的类型,它可以更快。

【讨论】:

  • 您的意思是“较慢”吗?
【解决方案4】:

有时是的。有一些它会被优化掉并且是一样的。对于 std::vector (和其他标准迭代器),它很可能会被优化为相同。

【讨论】:

    【解决方案5】:

    it++ 有可能会创建一个临时副本。

    此外,在 C++ 中,也有可能有人重载了后自增运算符。

    与预增量相比,这两件事都可能会降低性能。两者在实践中都不太重要。特别是临时副本将被大多数编译器优化掉,因为在 For 循环的第三个表达式中没有副作用。

    【讨论】:

      【解决方案6】:

      是的 ++它更高效,因为它++需要返回对象的副本然后自增。

      【讨论】:

        【解决方案7】:

        是的。据我记得,++it 比 it++ 效率更高,因为 it++ 会创建一个临时对象,而 ++it 不会。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-04-29
          • 1970-01-01
          • 1970-01-01
          • 2012-07-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多