-2
test321["abc"] = 1;
test321["abc"] = test321.count("abc") ? test321["abc"]++ : 0;

test321["abc"] = 1

test321["abc"] = 1;
test321["abc"] = test321.count("abc") ? test321["abc"]+1 : 0;

test321["abc"] = 2

Why is there a difference?

Sentinel
  • 441
  • 1
  • 6
  • 25
  • https://en.cppreference.com/w/cpp/language/operator_precedence , https://en.cppreference.com/w/cpp/language/operator_incdec – Jesper Juhl Aug 13 '18 at 20:03
  • 6
    `test321["abc"]++` returns the original value of `1`, even though the value stored is temporarily `2`., `test321["abc"]+1` returns `2`. Look up the difference between `x++` and `++x`, this will explain the issue (post vs pre increment operators) – Matthew Aug 13 '18 at 20:04
  • 1
    This question would be a lot simpler if you use an integer instead of a map – Tim Randall Aug 13 '18 at 20:29

2 Answers2

3

The line

test321["abc"] = test321.count("abc") ? test321["abc"]++ : 0;

has undefined behavior until C++17 since test321["abc"] is modified in two ways:

  1. By assignment.
  2. By the post increment operator.

It's best to avoid using such constructs. You can read more about it at Why are these constructs (using ++) undefined behavior in C?.

The second approach is well-behaved code and should be used for what you intend to do.

If you use C++17, both approaches should result in identical behavior.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • This is not correct since C++17. In C++17, the computation and side of effects of rhs are sequenced before lhs. – SergeyA Aug 13 '18 at 20:27
  • @SergeyA, Thanks for the correction. – R Sahu Aug 13 '18 at 20:29
  • @NathanOliver, looks like we have a different understanding of what "until" means. – R Sahu Aug 13 '18 at 20:37
  • 1
    @RSahu If something is undefined until C++X then it means in everything before C++X it is undefined behavior. Since it is defined behavior in C++17 and undefined in C++14 then it is undefined behavior until C++17. – NathanOliver Aug 13 '18 at 20:40
  • 1
    Another way to look at it. `until == [versionN, versionN+X)` – NathanOliver Aug 13 '18 at 20:43
  • @NathanOliver, For some reason, I though "until" meant "up to and including" but upon further digging at https://www.merriam-webster.com/dictionary/until, I realized it means "up to". Thanks for the LOTD :) – R Sahu Aug 13 '18 at 20:47
0

The question you're posing, "Why is there a difference?", is less meaningful than you might expect, because your code invokes Undefined Behavior.

Consider the following code snippet which is, semantically, equivalent to what your code is doing:

int x = 1;
x = x++;

What is a logical result for x? Is there a logical result?

Well, if you ask the C++ standard, the answer is that there is no logical result, and it leaves the result of an operation like this to be undefined. Any given compiler is given no restrictions or limitations on what it should do with code like this, so some compilers will work out that a logical result is for x to equal 2, and some compilers will work out 1 instead. Some compilers might (rarely) do something else entirely.

For more on this particular phenomenon, see this related Question.

To avoid undefined behavior, you should prefer constructs like this:

auto & ref = test321["abc"];//We save the reference to avoid performance issues 
//Note that the brackets operator will create an entry if it doesn't already exist, negating
//the need for a check to count(); count() will always return at least 1.
ref = 1;
ref = ref + 1;

Or

if(auto & ref = test321["abc"])
    ref++;//Will only increment if value was not 0.
Xirema
  • 19,889
  • 4
  • 32
  • 68