8

In the Google C++ Style Guide it says:

Preincrement and Predecrement

Use prefix form (++i) of the increment and decrement operators with iterators and other template objects.

When a variable is incremented (++i or i++) or decremented (--i or i--) and the value of the expression is not used, one must decide whether to preincrement (decrement) or postincrement (decrement).

When the return value is ignored, the "pre" form (++i) is never less efficient than the "post" form (i++), and is often more efficient. This is because post-increment (or decrement) requires a copy of i to be made, which is the value of the expression. If i is an iterator or other non-scalar type, copying i could be expensive. Since the two types of increment behave the same when the value is ignored, why not just always pre-increment?

The tradition developed, in C, of using post-increment when the expression value is not used, especially in for loops. Some find post-increment easier to read, since the "subject" (i) precedes the "verb" (++), just like in English.

For simple scalar (non-object) values there is no reason to prefer one form and we allow either. For iterators and other template types, use pre-increment.

I really must disagree with "When the return value is ignored, the "pre" form (++i) is never less efficient than the "post" form (i++), and is often more efficient. This is because post-increment (or decrement) requires a copy of i to be made, which is the value of the expression."

At least when it comes to pointers (and pointer registers can be used as integers).

Going back to my hardware days, there were plenty of machine instructions that had a form of addressing modes that had post-increment, post-decrement, and pre-decrement built in. The post-inc and post-dec modes cost 0 extra instruction cycles more than no increment at all. The incrementing happened after the instruction was executed while the next opcode was being fetched, but the pre-dec required an additional instruction cycle before the register was used. So, I don't really get it. Can someone explain google's case to me?

6
  • Unless your loop runs a million times in a real time shoot em up game nobody will notice the difference.
    – user949300
    Oct 7, 2017 at 6:27
  • @user949300 If you're doing anything a million times in a loop in a real time shoot'em up game, the problem ain't with post-increments. ;)
    – Neil
    Oct 9, 2017 at 12:16
  • 2
    Even when such a hardware instruction exists, what makes you think that the compiler can't use it when you use pre-increment? Especially given the qualification "when the return value is ignored" in the statement you contest. Oct 9, 2017 at 14:39
  • 2
    Bad advice. Correct use of the operator makes the intent of the code more clear, and ensures optimal performance in all cases, not merely some cases. Correct use means you don't use the post-increment unless you need the original value. Oct 9, 2017 at 16:58
  • 1
    The part about instruction fetch and pipeline stall sounds like it's an opinion written before out-of-order execution became mainstream, i.e. before year 2000. (I'm trying not to be ageism.)
    – rwong
    Oct 9, 2017 at 20:08

1 Answer 1

16

You may be right regarding pointers.

However:

  • C is not C++. C++ guarantees some very precise semantics especially around when copies are made, as this affects RAII: in C++, copies are an observable effect.

  • Not all iterators are pointers, but may be more complex objects. For example, output iterators like std::back_insert_iterator or iterators over non-contiguous data structures like std::unordered_map.

I really must disagree with "When the return value is ignored, the "pre" form (++i) is never less efficient than the "post" form (i++), and is often more efficient. This is because post-increment (or decrement) requires a copy of i to be made, which is the value of the expression."

at least when it comes to pointers.

There is nothing to disagree with here. The “++i is never less efficient than i++” just means that they may be equally efficient (which you argue for pointers). But if there is a difference, then ++i is expected to perform better.

To be precise, you argue that post-decrement may be more efficient on particular architectures that guarantee a more-efficient post-decrement than pre-decrement. However, such considerations only apply when you are developing for a particular microarchitecture. For portable code, the guarantees made by the language itself are more important. AFAIK neither x86 nor ARM instruction set families include a special post-increment instruction.

It is of course possible to create a counter-example where the pre-increment operator purposefully does unnecessary work, but for a consistent and minimal definition of post-increment and pre-increment, then post-increment can (and likely should) be implemented in terms of pre-increment:

struct X {
  ...
  // pre-increment
  X& operator++() { ...; return *this; }

  // post-increment
  X operator++(int) {
    X copy(*this);
    operator++();
    return copy;
  }
};

Since post-increment must return the state of the object before the increment was executed, a copy of the state prior to the increment is unavoidable for user-defined types.

Of course a sufficiently smart compiler may be able to inline the post-increment and avoid the copy (assuming a trivial copy ctor) by exploiting the sequencing rules and the as-if rule, but at best we are back to “equally efficient”.

1
  • 2
    X86 has a set of string instructions that include post increment and post decrement. The lodsb instruction does the equivalent of mov al,[esi] followed by inc esi.
    – cwallach
    Oct 7, 2017 at 17:31

Not the answer you're looking for? Browse other questions tagged or ask your own question.