为什么后置递增需要复制,而前置递增则不需要。

11

我知道这个问题已经被讨论了好几次,但我找不到一篇文章解释为什么在后增操作时需要进行复制。

引用 stackoverflow 上的回复:

int j = i++; // j will contain i, i will be incremented.
int j = ++i; // i will be incremented, and j will contain i+1.

当考虑到后增量/前增量的定义时,这个解释是非常合理的。在比较前增量和后增量的性能时,通常会说后增量需要复制一份副本、对其进行增量操作并返回该副本,而前增量仅增加数值而不创建副本。

尽管已经有许多帖子比较了它们的性能,但我确实找不到任何关于为什么必须在后增量中创建一个副本的解释。为什么不返回旧值,然后将变量的值增加1(或者通过重载运算符以任何其他方式),而不是创建一个新对象并返回它。


因为后置递增必须将先前的值保存在临时变量中,以便之后返回它。 - 101010
3
为什么不先返回旧值,然后再将变量的值增加一?如果你已经从函数返回了,怎么增加这个值呢? - AliciaBytes
@RaphaelMiedl 谢谢 Raphael,那正是我所错过的关键。我知道这是一些非常琐碎的东西 :) - ralzaul
可能是在C++中,i++和++i之间有性能差异吗?的重复问题。 - 101010
示例有些不好,因为编译器可以(几乎肯定会)将第一个变体轻松地转换为 int j = i; ++i,所以实际上没有临时变量(尽管编译器完全符合 as-if 规则)。但在某些情况下并不那么容易,特别是对于具有非平凡构造函数的类型,或者考虑到禁止这种自由指令重排序的屏障/内存模型。 - Damon
3个回答

13

区别在于someval++返回的是增加之前的值,要实现这个功能需要记住someval的值并进行复制。如果原始值没有存储在某个地方,那么你怎么能在更新值的同时返回原始值呢?


11

将前置增量运算符和后置增量运算符视为标准函数:

// ++i
int pre_increment(int &i) {
    i = i + 1;
    return i;
}

// i++
int post_increment(int &i) {
    int original_i = i;
    i = i + 1;
    return original_i;
}

这应该有助于你理解为什么在第二种情况(i++)中,必须在增量操作之前执行值的复制。


2

对于编译器如何优化前置和后置自增运算符,以使原始类型不一定需要进行复制,我很难说。

但是我可以展示用户定义类型的后置自增运算符需要进行复制的情况。

我们来简单看一下 std::vector::iterator

假设迭代器除了其他东西之外还存储了一个索引。

struct iterator
{
   size_t index;

   iterator operator++(int )
   {
      iterator copy = *this;
      this->index++;
      return copy;
   }
 };

在这种情况下,不可能先返回*this然后再增加索引。创建副本,增加this的索引,然后返回副本可以保留该操作的语义。

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