Why does setting an element of a map to its size increments the size *before* assigning it?

送分小仙女□ 提交于 2020-05-11 04:52:41

问题


This is a common pattern I use to index tokens as they come in: check if the token is in a map, and if not, add it to the map, assigning the map's size.

When doing this in C++, it unexpectedly increments the map's size before the assignment is made:

#include <cstdio>
#include <map>
using namespace std;

int main() {
    map<char, int> m;
    printf("Size before adding: %d\n", m.size());
    m['A'] = m.size();
    printf("Size after adding: %d\n", m.size());
    printf("What was added: %d\n", m['A']);

    return 0;
}

This prints out:

Size before adding: 0
Size after adding: 1
What was added: 1

The way I understand it, it should evaluate the right hand side, which is zero, then pass it to a function that puts 'A' and the zero into the map. But it seems to be evaluating it after it started assigning, which doesn't make sense.

Shouldn't the right-hand side be evaluated before the assignment operation?


回答1:


The behavior is unspecified, pedantically speaking.

But what happens in your case is this:

m['A'] = m.size();

m is a std::map which creates a new entry if the key doesn't exist.

In your case, the key 'A' doesn't exist, so it creates the entry, and return the reference to the value (which is default created) which then is assigned to m.size() in your case.

As said above, the behaviour is unspecified, as the order of evaluation of operands is unspecified which means m.size() might be evaluated before m['A']. If it does, then m['A'] will be 0, not 1.




回答2:


No.

(§5.17/1): "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."

Note, however, that while the assignment happens after the right and left operands are evaluated, no sequencing is specified between the evaluation of the left and right operands themselves. Therefore, the left could be evaluated first, then the right, or vice versa.




回答3:


But it seems to be evaluating it after it started assigning...

The order of evaluation in assignment is actually unspecified (whether the left hand or right-hand expression is evaluated first is unspecified), as indicated by the answers of this question.

If m.size() is evaluated first, then your code will work as intended, but you are not guaranteed of this behavior, and another implementation might evaluate m['A'] first, the same case with yours. These ambiguous cases must be avoided.

Better do something like this instead

auto size = m.size();
m['A'] = size;

which you are guaranteed that the size query is evaluated first before the element assignment.

LIVE CODE with the improvement..




回答4:


This is approximate implementation of [] operator, edited from stl header file

mapped_type& operator[](const key_type& key){
auto itr = lower_bound(key);
// itr->first is greater than or equivalent to key.
if (itr == end() || comp_func(key, (*itr).first))
      itr = insert(itr, value_type(key, mapped_type()));
return (*itr).second;
}

So as you can see for new element it inserts first, thereby increases map size by 1

Ref std::map::operator[]

If key does not match the key of any element in the container, the function inserts a new element with that key and returns a reference to its mapped value. Notice that this always increases the container size by one, even if no mapped value is assigned to the element (the element is constructed using its default constructor).

Edit :

As pointed out by others, m['A'] = m.size(); leads to an unspecified behavior,never use such statements, instead you can calculate the size first and then assign it to new key.



来源:https://stackoverflow.com/questions/18040037/why-does-setting-an-element-of-a-map-to-its-size-increments-the-size-before-as

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