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 m你能使用struct X{char c;int id;};设置s
?使用计数器生成id
。@BenjaminLindley:在这种情况下,首先是noi
。在这种情况下,这是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;
}