I know that compiler is usually the last thing to blame for bugs in a code, but I do not see any other explanation for the following behaviour of the following C code (distilled down from an actual project):
#include <iostream>
#include <map>
int main()
{
auto values = { 1, 3, 5 };
std::map<int, int> valMap;
for (auto const & val : values) {
std::cout << "before assignment: valMap.size() = " << valMap.size();
valMap[val] = valMap.size();
std::cout << " -> set valMap[" << val << "] to " << valMap[val] << "\n";
}
}
The expected output of this code is:
before assignment: valMap.size() = 0 -> set valMap[1] to 0
before assignment: valMap.size() = 1 -> set valMap[3] to 1
before assignment: valMap.size() = 2 -> set valMap[5] to 2
However, when I build a Release version with the (default) C 14 compiler, the output becomes:
before assignment: valMap.size() = 0 -> set valMap[1] to 1
before assignment: valMap.size() = 1 -> set valMap[3] to 2
before assignment: valMap.size() = 2 -> set valMap[5] to 3
In other words, all values in valMap
are larger by 1 than what they should be - it looks like the map gets appended before the right-hand-side of the assignment is evaluated.
This happens only in a Release build with C 14 language standard (which is the default in VS2019). Debug builds work fine (I hate when this happens - it took me hours to find out what is going on), as do Release builds of C 17 and C 20. This is why it looks like a bug to me.
My question is: is this a compiler bug, or am I doing something wrong/dangerous by using .size()
in the assignment?
CodePudding user response:
The evaluation order of A = B
was not specified before c 17, after c 17 B
is guaranteed to be evaluated before A
, see https://en.cppreference.com/w/cpp/language/eval_order rule 20.
The behaviour of valMap[val] = valMap.size();
is therefore unspecified in c 14, you should use:
auto size = valMap.size();
valMap[val] = size;
Or avoid the problem by using emplace
which is more explicit than relying on []
to automatically insert a value if it doesn't already exist:
valMap.emplace(val, size);