在STL映射中,使用map::insert比使用[]更好吗?
前段时间和同事讨论了如何在STL中插入值地图。 我更喜欢 map[key] = value;
,因为它感觉自然且易于阅读,而他更喜欢 map.insert(std::make_pair(key, value))
。
我刚刚问过他,我们都不记得为什么插入更好,但我确信这不仅仅是风格偏好,还有效率等技术原因。 SGI STL 参考简单地说:“严格来说,这个成员函数是不必要的:它的存在只是为了方便。”
谁能告诉我这个原因,还是我只是梦想有一个?
A while ago, I had a discussion with a colleague about how to insert values in STL maps. I preferred map[key] = value;
because it feels natural and is clear to read whereas he preferred map.insert(std::make_pair(key, value))
.
I just asked him and neither of us can remember the reason why insert is better, but I am sure it was not just a style preference rather there was a technical reason such as efficiency. The SGI STL reference simply says: "Strictly speaking, this member function is unnecessary: it exists only for convenience."
Can anybody tell me that reason, or am I just dreaming that there is one?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
当您编写时,
无法判断您是否替换了
键
的值
,或者是否创建了新的键
与值
。map::insert()
将仅创建:对于我的大多数应用,我通常不关心是创建还是替换,因此我使用更易于阅读的
map[key] = value
。When you write
there's no way to tell if you replaced the
value
forkey
, or if you created a newkey
withvalue
.map::insert()
will only create:For most of my apps, I usually don't care if I'm creating or replacing, so I use the easier to read
map[key] = value
.当涉及到映射中已经存在的键时,两者具有不同的语义。 所以它们实际上并没有直接可比性。
但是operator[]版本需要默认构造值,然后赋值,所以如果这比复制构造更昂贵,那么它会更昂贵。 有时默认构造没有意义,然后就不可能使用operator[]版本。
The two have different semantics when it comes to the key already existing in the map. So they aren't really directly comparable.
But the operator[] version requires default constructing the value, and then assigning, so if this is more expensive then copy construction, then it will be more expensive. Sometimes default construction doesn't make sense, and then it would be impossible to use the operator[] version.
std::map
需要注意的另一件事是:myMap[nonExistingKey];
将在地图中创建一个新条目,以nonExistingKey
初始化为默认值。当我第一次看到它时,这把我吓坏了(当我的头撞到一个令人讨厌的遗留错误时)。 没想到会这样。 对我来说,这看起来像是一个 get 操作,我没想到会有“副作用”。 从地图获取时首选
map.find()
。Another thing to note with
std::map
:myMap[nonExistingKey];
will create a new entry in the map, keyed tononExistingKey
initialized to a default value.This scared the hell out of me the first time I saw it (while banging my head against a nastly legacy bug). Wouldn't have expected it. To me, that looks like a get operation, and I didn't expect the "side-effect." Prefer
map.find()
when getting from your map.如果默认构造函数的性能影响不是问题,那么看在上帝的份上,请使用更具可读性的版本。
:)
If the performance hit of the default constructor isn't an issue, the please, for the love of god, go with the more readable version.
:)
insert
从异常安全的角度来看更好。表达式
map[key] = value
实际上是两个操作:map[key ]
- 创建具有默认值的地图元素。= value
- 将值复制到该元素中。第二步可能会出现异常。 结果,操作将仅完成部分(一个新元素已添加到映射中,但该元素未使用
value
进行初始化)。 当一个操作尚未完成,但系统状态被修改的情况,称为有“副作用”的操作。insert
操作提供了强有力的保证,意味着它没有副作用(https: //en.wikipedia.org/wiki/Exception_safety)。insert
要么完全完成,要么使地图处于未修改状态。http://www.cplusplus.com/reference/map/map/insert/:
insert
is better from the point of exception safety.The expression
map[key] = value
is actually two operations:map[key]
- creating a map element with default value.= value
- copying the value into that element.An exception may happen at the second step. As result the operation will be only partially done (a new element was added into map, but that element was not initialized with
value
). The situation when an operation is not complete, but the system state is modified, is called the operation with "side effect".insert
operation gives a strong guarantee, means it doesn't have side effects (https://en.wikipedia.org/wiki/Exception_safety).insert
is either completely done or it leaves the map in unmodified state.http://www.cplusplus.com/reference/map/map/insert/:
如果您的应用程序对速度至关重要,我会建议使用 [] 运算符,因为它会创建原始对象的总共 3 个副本,其中 2 个是临时对象,迟早会被销毁。
但在 insert() 中,创建了原始对象的 4 个副本,其中 3 个是临时对象(不一定是“临时对象”)并被销毁。
这意味着额外的时间:
1. 一个对象的内存分配
2. 一次额外的构造函数调用
3. 一次额外的析构函数调用
4. 一个对象的内存释放
如果你的对象很大,构造函数是典型的,析构函数会释放大量资源,以上几点更重要。 关于可读性,我认为两者都足够公平。
我想到了同样的问题,但不是关于可读性而是关于速度。
这是一个示例代码,通过它我了解了我提到的这一点。
If your application is speed critical i will advice using [] operator because it creates total 3 copies of the original object out of which 2 are temporary objects and sooner or later destroyed as.
But in insert(), 4 copies of the original object are created out of which 3 are temporary objects( not necessarily "temporaries") and are destroyed.
Which means extra time for:
1. One objects memory allocation
2. One extra constructor call
3. One extra destructor call
4. One objects memory deallocation
If your objects are large, constructors are typical, destructors do a lot of resource freeing, above points count even more. Regarding readability, i think both are fair enough.
The same question came into my mind but not over readability but speed.
Here is a sample code through which I came to know about the point i mentioned.
现在,在 c++11 中,我认为在 STL 映射中插入一对的最佳方法是:
结果 将是一对:
第一个元素 (result.first),指向插入或指向的对
如果键已存在,则为具有此键的对。
第二个元素 (result.second),如果插入正确则为 true 或
false 表示出了问题。
PS:如果您不关心顺序,您可以使用 std::unordered_map ;)
谢谢!
Now in c++11 I think that the best way to insert a pair in a STL map is:
The result will be a pair with:
First element (result.first), points to the pair inserted or point to
the pair with this key if the key already exist.
Second element (result.second), true if the insertion was correct or
false it something went wrong.
PS: If you don´t case about the order you can use std::unordered_map ;)
Thanks!
map::insert() 的一个问题是,如果键已存在于映射中,则它不会替换值。 我见过 Java 程序员编写的 C++ 代码,他们期望 insert() 的行为与 Java 中的 Map.put() 相同,其中值被替换。
A gotcha with map::insert() is that it won't replace a value if the key already exists in the map. I've seen C++ code written by Java programmers where they have expected insert() to behave the same way as Map.put() in Java where values are replaced.
需要注意的是,您还可以使用 Boost。分配:
One note is that you can also use Boost.Assign:
这是另一个示例,显示
operator[]
覆盖键的值(如果存在),但.insert
不会覆盖< /strong> 值(如果存在)。Here's another example, showing that
operator[]
overwrites the value for the key if it exists, but.insert
does not overwrite the value if it exists.这是一个相当有限的案例,但从我收到的评论来看,我认为这是值得注意的。
我过去见过人们以 的形式使用映射来
避免意外值覆盖的情况,但随后继续编写其他一些代码:
我记得他们这样做的原因是因为他们确信在这些代码中他们不会覆盖地图值的某些代码位; 因此,继续使用更“可读”的方法
[]
。实际上,我从未因这些人编写的代码而遇到任何直接麻烦,但直到今天我仍然强烈地感觉到,当风险可以轻易避免时,无论风险多么小,都不应该冒风险。
如果您处理的地图值绝对不能被覆盖,请使用
插入
。 不要仅仅为了可读性而设置例外。This is a rather restricted case, but judging from the comments I've received I think it's worth noting.
I've seen people in the past use maps in the form of
to evade cases of accidental value overwriting, but then go ahead writing in some other bits of code:
Their reason for doing this as I recall was because they were sure that in these certain bits of code they were not going to be overwriting map values; hence, going ahead with the more 'readable' method
[]
.I've never actually had any direct trouble from the code that was written by these people, but I strongly feel up until today that risks - however small - should not be taken when they can be easily avoided.
In cases where you're dealing with map values that absolutely must not be overwritten, use
insert
. Don't make exceptions merely for readability.事实上,std::map
insert()
函数不会覆盖与键关联的值,这使得我们可以编写如下的对象枚举代码:当我们需要映射不同的非对象时,这是一个非常常见的问题 。 0..N 范围内某些 id 的唯一对象。 这些 id 稍后可以使用,例如在图形算法中。 在我看来,使用
operator[]
替代方案看起来可读性较差:The fact that std::map
insert()
function doesn't overwrite value associated with the key allows us to write object enumeration code like this:It's a pretty common problem when we need to map different non-unique objects to some id's in range 0..N. Those id's can be later used, for example, in graph algorithms. Alternative with
operator[]
would look less readable in my opinion:insert()
和operator[]
已经很好了其他答案中已经解释过。 但是, 引入了std::map
的新插入方法C++11 和 C++17 分别:emplace()
正如 einpoklum 的评论中也提到的以及GutiMac 的回答。insert_or_assign()
和try_emplace()
。让我简单总结一下“新”插入方法:
emplace()
: 如果使用正确,此方法可以通过将元素构造为插入到位。 与insert()
类似,只有在容器中不存在具有相同键的元素时才会插入元素。insert_or_assign()
:此方法是operator[]
的“改进”版本。 与operator[]
不同,insert_or_assign()
不要求映射的值类型默认可构造。 这克服了Greg Rogers的回答中提到的缺点。try_emplace()
:此方法是emplace()
的“改进”版本。 与emplace()
不同,如果由于映射中已存在键而导致插入失败,try_emplace()
不会修改其参数(由于移动操作)。有关
insert_or_assign()
和try_emplace()
的更多详细信息,请参阅我的回答此处。Coliru 上的简单示例代码
The difference between
insert()
andoperator[]
has already been well explained in the other answers. However, new insertion methods forstd::map
were introduced with C++11 and C++17 respectively:emplace()
as also mentioned in einpoklum's comment and GutiMac's answer.insert_or_assign()
andtry_emplace()
.Let me give a brief summary of the "new" insertion methods:
emplace()
: When used correctly, this method can avoid unnecessary copy or move operations by constructing the element to be inserted in place. Similar toinsert()
, an element is only inserted if there is no element with the same key in the container.insert_or_assign()
: This method is an "improved" version ofoperator[]
. Unlikeoperator[]
,insert_or_assign()
doesn't require the map's value type to be default constructible. This overcomes the disadvantage mentioned e.g. in Greg Rogers' answer.try_emplace()
: This method is an "improved" version ofemplace()
. Unlikeemplace()
,try_emplace()
doesn't modify its arguments (due to move operations) if insertion fails due to a key already existing in the map.For more details on
insert_or_assign()
andtry_emplace()
please see my answer here.Simple example code on Coliru