插入vs emplace vs操作符[]在c ++映射 [英] insert vs emplace vs operator[] in c++ map

查看:480
本文介绍了插入vs emplace vs操作符[]在c ++映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我第一次使用地图,我意识到有很多方法来插入元素。您可以使用 emplace() operator [] insert(),以及使用 value_type make_pair 的变体。虽然有很多关于所有的信息和关于特定情况的问题,我仍然不能理解大局。
所以,我的两个问题是:


  1. 每个人的优点是什么? / p>


  2. 是否需要为标准添加emplace?



解决方案

映射的特定情况下,旧的选项只有两个: operator [] insert c $ c> insert )。



运算符[] 是一个查找或添加运算符。它将尝试在映射中找到具有给定键的元素,如果存在,它将返回对存储值的引用。如果没有,它将创建一个新的元素插入到默认初始化的位置并返回一个引用。



insert 函数(在单元素flavor中)采用 value_type std :: pair< const Key,Value> ),它使用键(第一成员)并尝试插入它。因为 std :: map 不允许重复,如果有一个现有元素,它不会插入任何东西。



两者之间的第一个区别是 operator [] 需要能够构造默认的初始化的,因此它不能用于值类型不能默认初始化。两者之间的第二个区别是当已经有一个具有给定键的元素时会发生什么。 insert 函数不会修改映射的状态,而是返回一个迭代器到元素(和 false 表示它没有被插入)。

  //假设m是std :: map< int,int&已经有一个带有键5和值0的元素
m [5] = 10; // postcondition:m [5] == 10
m.insert(std :: make_pair(5,15)); // m [5]仍然是10



该参数是 value_type 的一个对象,可以通过不同的方式创建。你可以直接使用合适的类型构造它,或传递任何可以构造 value_type 的对象,其中 std :: make_pair 起作用,因为它允许简单创建 std :: pair 对象,虽然它可能不是你想要的...



以下调用的净效果类似:

  K t; V u; 
std :: map< K,V> m; // std :: map< K,V> :: value_type是std :: pair< const K,V&

m.insert(std :: pair< const K,V>(t,u)); // 1
m.insert(std :: map< K,V> :: value_type(t,u)); // 2
m.insert(std :: make_pair(t,u)); // 3

但是不是真的相同... [1]和[2]实际上是等价的。在这两种情况下,代码创建一个相同类型的临时对象( std :: pair< const K,V> ),并将其传递给 insert 函数。 insert 函数将在二叉搜索树中创建相应的节点,然后将 value_type 部分从参数复制到节点。使用 value_type 的优点是, value_type 总是匹配 value_type ,您不能键入 std :: pair 参数的类型!



区别在于[3]。函数 std :: make_pair 是一个模板函数,将创建一个 std :: pair 。签名是:

 模板< typename T,typename U> 
std :: pair< T,U> make_pair(T const& t,U const& u);

我有意不提供模板参数 std :: make_pair ,因为这是常见的用法。这意味着模板参数是从调用中推导出来的,在这种情况下是 T == K,U == V ,因此调用 std :: make_pair 将返回一个 std :: pair< K,V> (注意缺少 / code>)。签名需要 value_type ,即 close ,但不等于调用的返回值std :: make_pair 。因为它足够接近它将创建一个临时的正确类型和副本初始化它。这将被复制到节点,共创建两个副本。



这可以通过提供模板参数来修复:

  m.insert(std :: make_pair< const K,V>(t,u)); // 4 

但是这仍然容易出错, 1]。



到目前为止,我们有不同的方法调用 insert ,需要创建 value_type 外部和该对象的副本到容器中。或者,如果类型是默认可构造可分配,则可以使用 operator [] $ c> m [k] = v ),它需要一个对象的默认初始化和该对象的值的 copy



在C ++ 11中,使用可变参数模板和完美的转发,有一种新的方式通过 emplacing (创建就绪)将元素添加到容器中。不同容器中的 emplace 函数基本上做同样的事情:而不是从中获取容器,函数接受将被转发到存储在容器中的对象的构造函数的参数。

  m.emplace(t,u); // 5 

在[5]中, std :: pair< const K,V> 不被创建并被传递到 emplace ,而是引用 t u 对象传递给 emplace ,将它们转发到 value_type 子对象内部的数据结构。在这种情况下, std :: pair< const K,V> 的副本被完成,这是 emplace 超过C ++ 03的选择。在插入的情况下,它不会覆盖地图中的值。






一个有趣的问题,我没有想过是如何 emplace 实际上可以实现一个地图,这不是一个简单的问题在一般情况下。


I'm using maps for the first time and I realized that there are many ways to insert an element. You can use emplace(), operator[] or insert(), plus variants like using value_type or make_pair. While there is a lot of information about all of them and questions about particular cases, I still can't understand the big picture. So, my two questions are:

  1. What is the advantage of each one of them over the others?

  2. Was there any need for adding emplace to the standard? Is there anything that wasn't possible before without it?

解决方案

In the particular case of a map the old options were only two: operator[] and insert (different flavors of insert). So I will start explaining those.

The operator[] is a find-or-add operator. It will try to find an element with the given key inside the map, and if it exists it will return a reference to the stored value. If it does not, it will create a new element inserted in place with default initialization and return a reference to it.

The insert function (in the single element flavor) takes a value_type (std::pair<const Key,Value>), it uses the key (first member) and tries to insert it. Because std::map does not allow for duplicates if there is an existing element it will not insert anything.

The first difference between the two is that operator[] needs to be able to construct a default initialized value, and it is thus unusable for value types that cannot be default initialized. The second difference between the two is what happens when there is already an element with the given key. The insert function will not modify the state of the map, but instead return an iterator to the element (and a false indicating that it was not inserted).

// assume m is std::map<int,int> already has an element with key 5 and value 0
m[5] = 10;                      // postcondition: m[5] == 10
m.insert(std::make_pair(5,15)); // m[5] is still 10

In the case of insert the argument is an object of value_type, which can be created in different ways. You can directly construct it with the appropriate type or pass any object from which the value_type can be constructed, which is where std::make_pair comes into play, as it allows for simple creation of std::pair objects, although it is probably not what you want...

The net effect of the following calls is similar:

K t; V u;
std::map<K,V> m;           // std::map<K,V>::value_type is std::pair<const K,V>

m.insert( std::pair<const K,V>(t,u) );      // 1
m.insert( std::map<K,V>::value_type(t,u) ); // 2
m.insert( std::make_pair(t,u) );            // 3

But the are not really the same... [1] and [2] are actually equivalent. In both cases the code creates a temporary object of the same type (std::pair<const K,V>) and passes it to the insert function. The insert function will create the appropriate node in the binary search tree and then copy the value_type part from the argument to the node. The advantage of using value_type is that, well, value_type always matches value_type, you cannot mistype the type of the std::pair arguments!

The difference is in [3]. The function std::make_pair is a template function that will create a std::pair. The signature is:

template <typename T, typename U>
std::pair<T,U> make_pair(T const & t, U const & u );

I have intentionally not provided the template arguments to std::make_pair, as that is the common usage. And the implication is that the template arguments are deduced from the call, in this case to be T==K,U==V, so the call to std::make_pair will return a std::pair<K,V> (note the missing const). The signature requires value_type that is close but not the same as the returned value from the call to std::make_pair. Because it is close enough it will create a temporary of the correct type and copy initialize it. That will in turn be copied to the node, creating a total of two copies.

This can be fixed by providing the template arguments:

m.insert( std::make_pair<const K,V>(t,u) );  // 4

But that is still error prone in the same way that explicitly typing the type in case [1].

Up to this point, we have different ways of calling insert that require the creation of the value_type externally and the copy of that object into the container. Alternatively you can use operator[] if the type is default constructible and assignable (intentionally focusing only in m[k]=v), and it requires the default initialization of one object and the copy of the value into that object.

In C++11, with variadic templates and perfect forwarding there is a new way of adding elements into a container by means of emplacing (creating in place). The emplace functions in the different containers do basically the same thing: instead of getting a source from which to copy into the container, the function takes the parameters that will be forwarded to the constructor of the object stored in the container.

m.emplace(t,u);               // 5

In [5], the std::pair<const K, V> is not created and passed to emplace, but rather references to the t and u object are passed to emplace that forwards them to the constructor of the value_type subobject inside the data structure. In this case no copies of the std::pair<const K,V> are done at all, which is the advantage of emplace over the C++03 alternatives. As in the case of insert it will not override the value in the map.


An interesting question that I had not thought about is how emplace can actually be implemented for a map, and that is not a simple problem in the general case.

这篇关于插入vs emplace vs操作符[]在c ++映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆