Is indexing a new map element and having something that reads it assigned to it undefined behaviour, or just unspecified?

心已入冬 提交于 2019-11-27 19:25:50

问题


After answering this question, there was a long discussion over whether the code in question was undefined behaviour or not. Here's the code:

std::map<string, size_t> word_count;
word_count["a"] = word_count.count("a") == 0 ? 1 : 2;

First of all, it was well-established that this was at least unspecified. The result differs based on which side of the assignment is evaluated first. In my answer, I followed through each of the four resulting cases, with factors of which side is evaluated first and whether the element exists prior to this.

There was a short form that came up as well:

(x = 0) = (x == 0) ? 1 : 2; //started as
(x = 0) = (y == "a") ? 1 : 2; //changed to

I claimed it was more like this:

(x = 0, x) = (x == 0) ? 1 : 2; //comma sequences x, like [] should

Eventually, I found an example that seemed to work for me:

i = (++i,i++,i); //well-defined per SO:Undefined Behaviour and Sequence Points

Back to the original, I broke it down into relevant function calls to make it easier to follow:

operator=(word_count.operator[]("a"), word_count.count("a") == 0 ? 1 : 2);
   ^       inserts element^                        ^reads same element
   |
assigns to element

If word_count["a"] does not exist, it was argued that it would be assigned to twice without a sequencing in between. I personally didn't see how that could happen if two things I thought were true actually were:

  1. When a side is picked to be evaluated, the whole side has to be evaluated before the other side can start.

  2. Constructs such as word_count["a"] = 1 exhibit well-defined behaviour, even in the case that an element is inserted and then assigned to.

Are these two statements true? Ultimately, is that actually undefined behaviour, and if it is, why does the second statement work (assuming it does)? If the second is false, I believe all the myMap[i]++;s in the world would be ill-formed.

Helpful Link: Undefined behavior and sequence points


回答1:


The behavior is unspecified, but not undefined.

Notice, that in the expression:

word_count["a"] = word_count.count("a") == 0 ? 1 : 2;
//              ^

The assignment operator marked with ^ is the built-in assignment operator, because std::map's operator [] returns a size_t&.

Per Paragraph 5.17/1 of the C++11 Standard on the built-in assignment operator(s):

The assignment operator (=) and the compound assignment operators all group right-to-left. [..] In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. With respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.

This means that in a built-in assignment such as:

a = b

First the operands are evaluated (in unspecified order), then the assignment is performed, and finally the value computation of the whole assignment expression is performed.

Considering the original expression:

word_count["a"] = word_count.count("a") == 0 ? 1 : 2;
//              ^

Because of the paragraph quoted above, in no case there are two unsequenced assignments to the same object: the assignment marked with ^ will always be sequenced after the assignment performed by operator [] (as part of the evaluation of the left hand side expression) in case the key "a" is not present in the map.

However, the expression will have a different outcome based on which side of the assignment is evaluated first. Thus, the behavior is unspecified, but not undefined.




回答2:


It is unspecified, but not undefined.

word_count.operator[]("a") and word_count.count("a") are function calls. Function executions are guaranteed by standard to not interleave - either first is fully sequenced before second or the other way around.

The specific definition can vary by standard, in C++11 relevant clause is in 1.9/15:

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.9

9) In other words, function executions do not interleave with each other.

indeterminately sequenced is defined in 1.9/13:

Evaluations A and B are indeterminately sequenced when either A is sequenced before B or B is sequenced before A , but it is unspecified which.

For example, evaluation of:

word_count["a"] = word_count.count("a");

consists of three parts:

  1. execution of word_count.operator[]("a")
  2. execution word_count.count("a")
  3. assignment

Let < mean 'is sequenced before'. Quoted part of the standard guarantees that either 1 < 2 or 2 < 1. Part quoted in @Andy Prowl answer also shows that both 1 < 3 and 2 < 3. So, there are only two options:

  • 1 < 2 < 3
  • 2 < 1 < 3

In both cases everything is fully sequenced and there is no chance for UB.



来源:https://stackoverflow.com/questions/15865627/is-indexing-a-new-map-element-and-having-something-that-reads-it-assigned-to-it

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!