为什么迭代器要使用前缀递增形式?

6

Johannes Schaub声称这里

对于那些定义未知的迭代器,始终使用前缀递增形式。这将确保您的代码尽可能地通用。

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    /* std::cout << *it; ... */
}

为什么不先迭代第一个元素,然后从v.begin() + 1开始循环?
3个回答

6

为什么不先迭代第一个元素,然后从v.begin() + 1开始循环?

迭代语句总是在每次迭代结束时执行。这与你使用的递增运算符的类型无关,或者你是否使用递增运算符。

迭代语句表达式的结果不会被使用,因此它对循环行为没有影响。以下语句:

++it;

功能等同于该语句:

it++;

后缀和前缀递增表达式只有在使用表达式结果时才有不同的行为。


为什么要对迭代器使用前缀递增形式?

因为后缀操作意味着复制。复制一个迭代器通常至少与不复制一个迭代器一样慢,但潜在地更慢。

后缀递增的典型实现:

iterator tmp(*this); // copy
++(*this);           // prefix increment
return tmp;          // return copy of the temporary
                     // (this copy can be elided by NRVO)

如果结果没有被使用,即使第一次复制也可以进行优化,但只有当操作在内联扩展时才能保证。


我不会盲目地使用“始终使用迭代器前缀增量”的规则。有些算法用后缀更清晰,尽管这只是我的意见。适合使用后缀增量的算法示例:

template<class InIter, class OutIter>
OutIter copy(InIter first, InIter last, OutIter out) {
    while(first != last)
        *out++ = *first++;
    return out;
}

5
请注意,您的代码等效于
for(std::vector<T>::iterator it = v.begin(); it != v.end(); ) {
    /* std::cout << *it; ... */
    ++it;
}

而且很明显,你写++it;或者it++;都没关系。(这也回答了你的最后一个问题。)
但是,从概念上讲,it++在其实现中需要存储未递增值的副本,因为这就是表达式的求值结果。 it可能是一个大而笨重的对象,复制它的值会消耗计算资源,而且你的编译器可能无法优化it++隐含的值复制。
现在,对于大多数容器,如果表达式的值没有被使用,编译器将把更易读的it++优化为++it;也就是说,生成的代码将是相同的。
我遵循作者的建议,尽可能使用前缀递增,但我(i)比较传统,(ii)知道许多专家程序员不这样做,所以这主要取决于个人选择。

1
因为这个for循环将被解析为:

为什么不先迭代第一个,然后从v.begin() + 1开始循环?

{
  init_statement 
  while ( condition ) { 
    statement 
    iteration_expression ; 
  }
}

所以。
for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    /* std::cout << *it; ... */
}

等同于

{
  std::vector<T>::iterator it = v.begin();
  while ( it != v.end() ) { 
    /* std::cout << *it; ... */
    ++it ; 
  }
}

这意味着它会首先在v.begin()处执行循环,然后向前移动it。前缀递增意味着增加值,然后返回增加对象的引用;由于在此情况下未使用返回的对象,则++itit++将导致相同的结果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接