4
std::map<int,int> bar;

int foo(int key)
{
  bar.erase(key);
  return 1;
}    

int main()
{
  bar[0] = foo(0);
  return 0;
}

This code compiled with GCC 4.8 segs fault when checking memory usage with electric fence.

LD_PRELOAD=libefence.so.0.0 ./a.out

The problem comes from the fact that the compiler generates a code that starts to allocate a new entry in the map, then executes foo() to get the value to put into bar[0]. While running foo(), the entry gets destroyed and the code finally ends by writing in non-allocated memory.

Does the way the operations are ordered depend on the compiler implementation, or is it specified by the C++ current standard?

DavidH
  • 415
  • 4
  • 21
Slek
  • 63
  • 6
  • 1
    See also http://stackoverflow.com/a/4183735. – Kerrek SB Oct 14 '15 at 17:41
  • Look into sequencing and sequence points. It's undefined whether `bar[0]` will be evaluated before `foo(0)` if it is (and it looks like it is for you) then you're in big trouble. – AndyG Oct 14 '15 at 17:43
  • @JasonMc92 - There isn't a single occurrence of either `new` or `delete` in OP! – Happy Green Kid Naps Oct 14 '15 at 18:06
  • @HappyGreenKidNaps, argh, I misread "erase" as "delete", Darned dyslexia. Sorry about that. *Slumps down and takes a long drink of coffee.* – CodeMouse92 Oct 14 '15 at 18:08
  • You know, I have to agree with @KerrekSB here. Curiosity's great and it's nice to know why doing something that looks iffy and did not work won't work, but sometimes the simple solution's the best. – user4581301 Oct 14 '15 at 18:12

1 Answers1

5

The standard (§1.9 15) specifies that the evaluation of the two operands to a binary operator is unsequenced (unless in some specific cases):

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.

This means that it does not mandate that one side of the assignment operation is evaluated before the other, and in fact, it is undefined behavior to depend on the order of these unsequenced operations.

This is also generally true for the order of evaluation of function arguments.

You need to break your assignment in two:

int result = foo(0);
bar[0] = result;
zneak
  • 134,922
  • 42
  • 253
  • 328
  • "and in fact, it is undefined behavior to depend on the order of the operations" -- It's not, generally. The order of the two function calls is unspecified, but that only means you can't reliably tell which function will be called first. It just so happens that in the OP's case, depending on the order, a dangling reference will be accessed. *That* renders the behaviour undefined, not the mere fact of depending on a specific order of operations. Even if I want `printf("Hello, ") + printf("world!")` to print `Hello, world!`, I am guaranteed to either the intended output or `world!Hello, `. –  Oct 14 '15 at 18:00
  • @hvd, yes, I meant that it's UB to depend on the order of unsequenced operations. – zneak Oct 14 '15 at 18:03
  • Oh, oops, terminology mix-up. That's "indeterminately sequenced", not "unsequenced", both in my example and in the OP's code, because of the function calls. Do you still think it's UB then? –  Oct 14 '15 at 18:07
  • Yes, it says "Except where noted", and also contains "Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function." later in that same p15. –  Oct 14 '15 at 18:14