Strange std::map behaviour

c++, g++, visual-studio-2012

Solution

This is actually a well-formed program that has two equally valid execution paths, so both compilers are right.

a[1] = a.size()

In this expression, the evaluation of the two operands of `=` are unsequenced.

§1.9/15 [intro.execution] Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.

However, function calls are not interleaved, so the calls to `operator[]` and `size` are actually indeterminately sequenced, rather than unsequenced.

§1.9/15 [intro.execution] 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.

This means that the function calls may happen in one of two orders:

- `operator[]` then `size`

- `size` then `operator[]`

If a key doesn't exist and you call `operator[]` with that key, it will be added to the map, thereby changing the size of the map. So in the first case, the key will be added, the size will be retrieved (which is 1 now), and `1` will be assigned to that key. In the second case, the size will be retrieved (which is 0), the key will be added, and `0` will be assigned to that key.

Note, this is not a situation that brings about undefined behaviour. Undefined behaviour occurs when two modifications or a modification and a read of the same scalar object are unsequenced.

§1.9/15 [intro.execution] If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

In this situation, they are not unsequenced but indeterminately sequenced.

So what we do have is two equally valid orderings of the execution of the program. Either could happen and both give valid output. This is unspecified behaviour.

§1.3.25 [defns.unspecified] unspecified behavior behavior, for a well-formed program construct and correct data, that depends on the implementation

So to answer your questions:

Which compiler is right?

Both of them are.

Am I doing something wrong?

Probably. It's unlikely that you would want to write code that has two execution paths like this. Unspecified behaviour can be okay, unlike undefined behaviour, because it can be resolved to a single observable output, but it's not worth having in the first place if you can avoid it. Instead, don't write code that has this kind of ambiguity. Depending on what exactly you want correct path to be, you can do either of the following:

auto size = a.size();
a[1] = size; // value is 0

Or:

a[1];
a[1] = a.size(); // value is 1

If you want the result to be `1` and you know the key doesn't yet exist, you could of course do the first code but assign `size + 1`.

Problem

The following test program ``` #include <map> #include <iostream> using namespace std; int main(int argc, char **argv) { map<int,int> a; a[1]=a.size(); for(map<int,int>::const_iterator it=a.begin(); it!=a.end(); ++it) cout << "first " << (*it).first << " second " << (*it).second << endl; } ``` leads to different output when compiled on `g++ 4.8.1` (Ubuntu 12.04 LTS): ``` g++ xxx.cpp ./a.out first 1 second 1 ``` and on Visual Studio 2012 (Windows 7) (Standard Win32 Console Application Project): ``` ConsoleApplication1.exe first 1 second 0 ``` Which compiler is right? Am I doing something wrong?

Original source

Related problems