Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/128.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jsf-2/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么将贴图的元素设置为其大小会在*分配它之前增加大小*?_C++_Map_Assignment Operator - Fatal编程技术网

C++ 为什么将贴图的元素设置为其大小会在*分配它之前增加大小*?

C++ 为什么将贴图的元素设置为其大小会在*分配它之前增加大小*?,c++,map,assignment-operator,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[

这是一种常见的模式,我使用它来索引令牌:检查令牌是否在映射中,如果不在映射中,则将其添加到映射中,并指定映射的大小

在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;
}
按照我的理解,它应该计算右边的值,即零,然后将其传递给一个函数,该函数将“a”和零放入映射中。但它似乎是在开始分配后对其进行评估,这是没有意义的


右手边不应该在赋值操作之前进行评估吗?

从学究的角度讲,该行为是未指定的。

但你的情况是:

m['A'] = m.size();
m
是一个
std::map
,如果密钥不存在,它将创建一个新条目

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

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

但它似乎是在开始分配后进行评估的

赋值中的求值顺序实际上是未指定的(无论是先求值左手表达式还是先求值右手表达式都未指定),如的答案所示

如果首先计算
m.size()
,那么您的代码将按预期工作,但您不能保证这种行为,另一个实现可能会首先计算
m['A']
,与您的情况相同。必须避免这些模棱两可的情况

最好还是这样做吧

auto size = m.size();
m['A'] = size;
可以保证在元素赋值之前首先计算大小查询

.

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


但是,请注意,虽然赋值发生在求值右操作数和左操作数之后,但在求值左操作数和右操作数之间不指定排序。因此,可以先计算左侧,然后计算右侧,反之亦然。

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

所以,正如您所看到的,对于新元素,它首先插入,从而将贴图大小增加1

参考号

如果键与容器中任何元素的键不匹配,则 函数使用该键插入新元素并返回引用 到它的映射值。请注意,这始终会增加容器的容量 大小为1,即使没有为元素指定映射值( 元素是使用其默认构造函数构造的)

编辑:


正如其他人指出的,
m['A']=m.size()
会导致未指定的行为,请不要使用此类语句,而是可以先计算大小,然后将其分配给新键

不,我很确定这是UB。@Borgeader:这是未指定的行为。@Nawaz哦,对了,未指定的想法。而不是
map mstruct X{char c;int id;};设置s
?使用计数器生成
id
。@BenjaminLindley:在这种情况下,首先是no
i
。在这种情况下,这是
m
,它是用户定义的类型。它出现在两边。
size\u type
的创建和初始化发生在函数
operator[]
的内部,之后是序列点,实际上是许多序列点,然后赋值发生在用户代码中(函数外部)。它们之间有许多序列点。我不知道UB。对,操作数的评价顺序在C++中没有具体说明吗?@ EvGeNeSeleGeV:是的,所有操作符都是正确的,除了逗号操作符<代码>,<代码>。在这种情况下,顺序被定义为左到右…另外,对于<代码>或(<代码> < <代码> >,<代码>和(<代码> &和代码> >)操作符也定义了顺序。@ EvgeniSergeev:C++没有定义函数参数的顺序,也可以用诸如代码>赋值(key,value)之类的东西来定义;
仍按未指定的顺序计算。这就是为什么您通常应该避免重载
运算符、
运算符| |
运算符&&
——您失去了对普通运算符的顺序保证。在C++17中,这是现在定义的行为,因为左手边的顺序保证在右手边之前。你介意更新一下答案吗?如果你愿意,我也可以?谢谢
auto size = m.size();
m['A'] = size;
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;
}