7

I recently saw a piece of code at comp.lang.c++ moderated returning a reference of a static integer from a function. The code was something like this

int& f()
{
   static int x;
   x++;
   return x;
}

int main()
{
  f()+=1; //A
  f()=f()+1; //B
  std::cout<<f();

}

When I debugged the application using my cool Visual Studio debugger I saw just one call to statement A and guess what I was shocked. I always thought i+=1 was equal to i=i+1 so f()+=1 would be equal to f()=f()+1 and I would be seeing two calls to f(), but I saw only one. What the heck is this? Am I crazy or is my debugger gone crazy or is this a result of premature optimization?

casperOne
  • 73,706
  • 19
  • 184
  • 253
Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
  • 4
    `static int x` is uninitialized. – Kirill V. Lyadvinsky Aug 09 '10 at 14:01
  • 1
    That's not related to the question, though. – BjoernD Aug 09 '10 at 14:07
  • 1
    @Rambo: I think you are talking about this thread: http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/566222d9b756ecc5/3279970278f46946 – Prasoon Saurav Aug 09 '10 at 14:12
  • 8
    @Kirill: static variables are zero-initialized if you don't specify another value. Actually, if you want to get technical, they're zero-initialized anyway, and only afterwards initialized to some other value if you specify that (one of the rare places that the same variable can be initialized twice to two different values legitimately. – Jerry Coffin Aug 09 '10 at 14:14
  • 1
    For terminology's sake, "premature optimization" is necessarily a fault of judgment, and applies only to optimizations made by programmers (trying to optimize something that has no significant impact on program speed, and making things unreadable or harder to maintain in the process). A compiler's optimizations have no impact on the source code, and so are either correct or incorrect (a bug). – Tyler McHenry Aug 09 '10 at 16:00

5 Answers5

26

This is what The Standard says about += and friends:

5.17-7: The behavior of an expression of the form E1 op= E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.[...]

So the compiler is right on that.

Nordic Mainframe
  • 28,058
  • 10
  • 66
  • 83
10

i+=1 is functionally the same as i=i+1. It's actually implemented differently (basically, it's designed to take advantage of CPU level optimization).

But essencially the left side is evaluated only once. It yields a non-const l-value, which is all it needs to read the value, add one and write it back.

This is more obvious when you create an overloaded operator for a custom type. operator+= modifies the this instance. operator+ returns a new instance. It is generally recommended (in C++) to write the oop+= first, and then write op+ in terms of it.

(Note this is applies only to C++; in C#, op+= is exactly as you assumed: just a short hand for op+, and you cannot create your own op+=. It is automatically created for you out of the Op+)

James Curran
  • 101,701
  • 37
  • 181
  • 258
9

Your thinking is logical but not correct.

i += 1;
// This is logically equivalent to:
i = i + 1;

But logically equivalent and identical are not the same.
The code should be looked at as looking like this:

int& x = f();
x += x;
// Now you can use logical equivalence.
int& x= f();
x = x + 1;

The compiler will not make two function calls unless you explicitly put two function calls into the code. If you have side effects in your functions (like you do) and the compiler started adding extra hard to see implicit calls it would be very hard to actually understand the flow of the code and thus make maintenance very hard.

Martin York
  • 257,169
  • 86
  • 333
  • 562
3

f() returns a reference to the static integer. Then += 1 adds one to this memory location – there's no need to call it twice in statement A.

Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
BjoernD
  • 4,720
  • 27
  • 32
0

In every language I've seen which supports a += operator, the compiler evaluates the operand of the left-hand side once to yield some type of a address which is then used both to read the old value and write the new one. The += operator is not just syntactic sugar; as you note, it can achieve expression semantics which would be awkward to achieve via other means.

Incidentally, the "With" statements in vb.net and Pascal both have a similar feature. A statement like:

' Assime Foo is an array of some type of structure, Bar is a function, and Boz is a variable.
  With Foo(Bar(Boz))
    .Fnord = 9
    .Quack = 10
  End With
will compute the address of Foo(Bar(Boz)), and then set two fields of that structure to the values nine and ten. It would be equivalent in C to
  {
    FOOTYPE *tmp = Foo(Bar(Boz));
    tmp->Fnord = 9;
    tmp->Quack = 10;
  }

but vb.net and Pascal do not expose the temporary pointer. While one could achieve the same effect in VB.net without using "With" to hold the result of Bar(), using "With" allows one to avoid the temporary variable.

supercat
  • 77,689
  • 9
  • 166
  • 211