为什么在分配地图之前将地图元素设置为其大小会增加大小*?

时间:2013-08-04 05:37:29

标签: c++ map assignment-operator

这是我用来索引令牌的常用模式:检查令牌是否在地图中,如果没有,则将其添加到地图中,分配地图的大小。

在C ++中执行此操作时,在进行分配之前意外地增加了地图的大小

#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;
}

打印出来:

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

我理解它的方式,它应评估右侧,即零,然后将其传递给将“A”和零置于地图中的函数。但它似乎是在开始分配之后对其进行评估,这没有任何意义。

在分配操作之前,不应评估右侧吗?

4 个答案:

答案 0 :(得分:6)

这种行为是未指明的,迂腐地说。

但你的情况会发生什么:

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

mstd::map,如果密钥不存在,则会创建新条目。

在您的情况下,键'A'不存在,因此它创建条目,并返回对值的引用(默认创建),然后将其分配给您的m.size()情况下。

如上所述,行为未指定,因为操作数的评估顺序未指定,这意味着m.size()可能会在m['A']之前进行评估。如果是,则m['A']将为0,而不是1

答案 1 :(得分:4)

  

但它似乎是在开始分配之后对其进行评估......

分配中的评估顺序实际上未指定(是否首先评估左手或右手表达是未指定的),如this question的答案所示。

如果首先评估m.size(),那么您的代码将按预期工作,但您无法保证此行为,而另一个实现可能首先评估m['A'],与您的情况相同。必须避免这些模糊的情况。

更好地做这样的事情

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

保证在元素赋值之前首先计算大小查询。

LIVE CODE with the improvement.

答案 2 :(得分:4)

没有

(§5.17/ 1):“在所有情况下,赋值在右和左操作数的值计算之后,以及赋值表达式的值计算之前排序。”

但请注意,虽然在评估右和左操作数之后进行赋值,但在左右操作数本身的评估之间未指定排序。因此,左边可以先评估,然后右边评估,反之亦然。

答案 3 :(得分:1)

这是[]运算符的近似实现,从stl头文件编辑

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;
}

因此,您可以看到它首先插入新元素,从而将地图大小增加1

参考std::map::operator[]

  

如果key与容器中任何元素的键不匹配,则为   function使用该键插入一个新元素并返回一个引用   到它的映射值。请注意,这总是会增加容器   即使没有为元素分配映射值,也将大小加1   element是使用其默认构造函数构造的。

修改:

正如其他人所指出的那样,m['A'] = m.size();导致一个未指定的行为,从不使用这样的语句,而是先计算大小然后再将其分配给新密钥。