优化 i++ 有意义吗? as ++i 以避免临时变量?

发布于 2024-09-24 00:37:20 字数 296 浏览 1 评论 0原文

有人告诉我,我可以写

for (iterator it = somecontainer.begin(); it != somecontainer.end(); ++it)

而不是

for (iterator it = somecontainer.begin(); it != somecontainer.end(); it++)

...,因为后者会产生额外未使用的临时变量的成本。这种优化对于现代编译器有用吗?写代码的时候需要考虑这个优化吗?

Someone told me that I can write

for (iterator it = somecontainer.begin(); it != somecontainer.end(); ++it)

instead of

for (iterator it = somecontainer.begin(); it != somecontainer.end(); it++)

...since the latter one has the cost of an extra unused temporary variable. Is this optimization useful for modern compiler? Do I need to consider this optimization when writing code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

找回味觉 2024-10-01 00:37:20

这是一个值得养成的好习惯,因为迭代器可能任意复杂。对于 vector::iteratorint 索引,不,不会有什么区别。

编译器永远无法消除(消除)副本,因为复制消除仅消除中间临时对象,而不消除未使用的临时对象。对于包括大多数迭代器在内的轻量级对象,编译器可以优化实现副本的代码。然而,当它不能时,它并不总是显而易见的。例如,后递增的istream_iterator保证复制最后读取的string对象。在非引用计数的 string 实现上,它将分配、复制并立即释放内存。这个问题更有可能适用于支持后增量的较重的非迭代器类。

当然没有缺点。在过去的一两年里,它似乎已经成为主流风格。

It's a good habit to get into, since iterators may be arbitrarily complex. For vector::iterator or int indexes, no, it won't make a difference.

The compiler can never eliminate (elide) the copy because copy elision only eliminates intermediate temporaries, not unused ones. For lightweight objects including most iterators, the compiler can optimize out the code implementing the copy. However, it isn't always obvious when it can't. For example, post-incrementing istream_iterator<string> is guaranteed to copy the last string object read. On a non-reference-counted string implementation, that will allocate, copy, and immediately free memory. The issue is even more likely to apply to heavier, non-iterator classes supporting post-increment.

There is certainly no disadvantage. It seems to have become the predominant style over the past decade or two.

感性不性感 2024-10-01 00:37:20

我通常不认为前缀 ++优化。这就是我默认编写的内容,因为它可能更快,而且编写起来也很容易,而且没有任何缺点。

但我怀疑是否值得返回并更改现有代码以使用前缀版本。

I don't normally consider the prefix ++ an optimization. It's just what I write by default, because it might be faster, it's just as easy to write, and there's no downside.

But I doubt it's worth going back and changing existing code to use the prefix version.

々眼睛长脚气 2024-10-01 00:37:20

是的,从概念上讲,这是正确的做法。您关心它是 i++ 还是 ++i 吗?不,你不知道。哪一个更好?第二个更好,因为它可能更快。所以你选择第二个(预增量)。

在典型情况下,发出的代码不会有任何差异,但迭代器可以具有任何实现。你无法控制它,也无法控制编译器是否会生成好的代码。您可以控制的是如何传达您的意图。这里不需要后置增量。

Yes, that's conceptually the right thing to do. Do you care if it's i++ or ++i? No, you don't. Which one is better? The second one is better since it's potentially faster. So you choose the second (pre-increment).

In typical cases there will be no difference in emitted code, but the iterators can have whatever implementation. You can't control it and you can't control whether the compiler will emit good code. What you can control is how you conveys your intent. You don't need the post-increment here.

小矜持 2024-10-01 00:37:20

不。(从风格上来说,我更喜欢它,因为我的母语是英语,它主要是动词先于名词的语言,因此“increment it”比“increment it”更容易读起来it 增量”。但这就是风格和主观性。)

但是,除非您在循环中更改 somecontainer 的内容,否则您可能会考虑获取 的返回值>somecontainer.end() 到临时变量中。您在那里所做的实际上会在每个循环上调用该函数。

No. (Stylistically I prefer it, because my native language is English which is mostly a verb-precedes-noun language, and so "increment it" reads more easily than "it increment". But that's style and subjective.)

However, unless you're changing somecontainer's contents in your loop, you might consider grabbing the return value of somecontainer.end() into a temporary variable. What you're doing there will actually call the function on every loop.

十雾 2024-10-01 00:37:20

这取决于您要应用 operator++ 的类型。如果您谈论的是用户定义类型,则将涉及复制整个 UDT,这可能非常昂贵。

但是,如果您谈论的是内置类型,那么可能根本没有区别。

养成尽可能使用前增量的习惯可能是件好事,即使后增量没问题,只是为了养成习惯并让代码看起来一致。您应该依靠编译器来为您优化一些东西,但是没有理由让它的工作变得更加困难,而不是无缘无故地变得更加困难。

It depends on the type to which you're applying operator++. If you are talking about a user defined type, that's going to involve copying the entire UDT, which can be very expensive.

However, if you are talking about a builtin type, then there's likely no difference at all.

It's probably good to get in the habit of using preincrement where possible even when postincrement is fine, simply to be in the habit and to give your code a consistent look. You should be leaning on the compiler to optimize some things for you, but there's no reason to make it's job harder than it has to be for no reason whatsoever.

染年凉城似染瑾 2024-10-01 00:37:20

考虑后递增通常如何在用户定义类型中实现:

MyIterator operator++(int) {
    MyIterator retval(*this);
    ++*this;
    return retval;
}

因此我们有两个问题:我的编译器可以在未使用返回值的情况下对此进行优化,并且会 > 我的编译器会在未使用返回值的情况下对此进行优化吗?

至于“可以吗?”,肯定有不可以的情况。如果代码没有内联,我们就不走运了。如果 MyIterator 的复制构造函数具有可观察到的副作用,那么我们就不走运了 - 复制构造函数省略允许就地构造返回值和临时对象的副本,因此至少该值可能只会被复制一次。但它不允许根本不构造返回值。根据您的编译器,内存分配很可能是一个可观察到的副作用。

当然,迭代器通常旨在内联,并且迭代器通常是轻量级的,不需要任何努力来复制。只要我们在这两点上都没有问题,我认为我们就可以正常工作 - 编译器可以内联代码,发现 retval 未被使用,并且它的创建没有副作用,并且删除它。这仅留下预增量。

至于“会吗?” - 在你的编译器上尝试一下。我不会被打扰,因为我总是使用预增量,其中两者是等效的;-)

对于“编写代码时是否需要考虑这种优化?”,我会说不是这样,但过早的悲观化就像过早优化是一个坏习惯。仅仅因为一个(最多)浪费时间并不意味着另一个是高尚的 - 不要仅仅因为你可以就竭尽全力让你的代码变慢。如果有人真的喜欢在循环中看到 i++ ,那么他们的代码不太可能变慢,因此如果必须的话,他们可以针对美观进行优化。就我个人而言,我更希望人们提高他们的品味......

Consider how post-increment is typically implemented in a user-defined type:

MyIterator operator++(int) {
    MyIterator retval(*this);
    ++*this;
    return retval;
}

So we have two questions: can my compiler optimise this in cases where the return value is unused, and will my compiler optimise this in cases where the return value is unused?

As for "can it?", there certainly are cases where it can't. If the code isn't inlined, we're out of luck. If the copy constructor of MyIterator has observable side effects then we're out of luck - copy constructor elision allows return values and copies of temporaries to be constructed in place, so at least the value might only be copied once. But it doesn't allow return values not to be constructed at all. Depending on your compiler, memory allocation might well be an observable side-effect.

Of course iterators usually are intended to be inlined, and iterators usually are lightweight and won't take any effort to copy. As long as we're OK on these two counts, I think we're in business - the compiler can inline the code, spot that retval is unused and that its creation is side-effect free, and remove it. This just leaves a pre-increment.

As for "will it?" - try it on your compiler. I can't be bothered, because I always use pre-increment where the two are equivalent ;-)

For "do I need to consider this optimization when writing code?", I would say not as such, but that premature pessimization is as bad a habit as premature optimization. Just because one is (at best) a waste of time does not mean that the other is noble - don't go out of your way to make your code slower just because you can. If someone genuinely prefers to see i++ in a loop then it's fairly unlikely ever to make their code slower, so they can optimise for aesthetics if they must. Personally I'd prefer that people improve their taste...

苏别ゝ 2024-10-01 00:37:20

它实际上会对未优化的代码(即调试版本)产生影响。

It will actually make a difference in unoptimized code, which is to say debug builds.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文