問題描述
我第一次使用地圖,我意識到有很多方法可以插入一個元素.您可以使用 emplace()
、operator[]
或 insert()
,以及使用 value_type
或 <代碼>make_pair.雖然有很多關于所有這些的信息和關于特定案例的問題,但我仍然無法理解大局.所以,我的兩個問題是:
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:
它們中的每一個相對于其他的有什么優勢?
What is the advantage of each one of them over the others?
是否需要將 emplace 添加到標準中?沒有它,以前有什么是不可能的嗎?
Was there any need for adding emplace to the standard? Is there anything that wasn't possible before without it?
推薦答案
在地圖的特殊情況下,舊的選項只有兩個:operator[]
和 insert
(insert
的不同風格).所以我將開始解釋這些.
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.
operator[]
是一個 find-or-add 操作符.它將嘗試在地圖內找到具有給定鍵的元素,如果存在,它將返回對存儲值的引用.如果沒有,它將創建一個新元素插入到位并使用默認初始化并返回對它的引用.
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.
insert
函數(在單元素風格中)接受一個 value_type
(std::pair
),它使用密鑰(first
成員)并嘗試插入它.因為 std::map
不允許重復,如果有一個現有元素,它不會插入任何東西.
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.
兩者的第一個區別是operator[]
需要能夠構造一個默認初始化的value,因此對于不能被初始化的值類型是不可用的默認初始化.兩者之間的第二個區別是當已經存在具有給定鍵的元素時會發生什么.insert
函數不會修改地圖的狀態,而是返回一個迭代器到元素(以及一個 false
表示它沒有被插入).
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
在insert
的情況下,參數是一個value_type
的對象,可以用不同的方式創建.您可以使用適當的類型直接構造它或傳遞可以從中構造 value_type
的任何對象,這是 std::make_pair
發揮作用的地方,因為它允許std::pair
對象的簡單創建,雖然它可能不是你想要的......
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...
以下調用的凈效果類似:
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
但實際上并不相同... [1] 和 [2] 實際上是等效的.在這兩種情況下,代碼都會創建一個相同類型的臨時對象 (std::pair
) 并將其傳遞給 insert
函數.insert
函數將在二叉搜索樹中創建適當的節點,然后將 value_type
部分從參數復制到節點.使用 value_type
的好處是,value_type
總是匹配 value_type
,你不能錯誤輸入std::pair
參數!
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!
不同之處在于 [3].函數 std::make_pair
是一個模板函數,它將創建一個 std::pair
.簽名是:
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 );
我故意不向 std::make_pair
提供模板參數,因為這是常見用法.這意味著模板參數是從調用中推導出來的,在這種情況下是 T==K,U==V
,所以調用 std::make_pair
將返回一個 std::pair
(注意缺少的 const
).簽名要求 value_type
close 但與調用 std::make_pair
的返回值不同.因為它足夠接近它會創建一個正確類型的臨時文件并復制初始化它.這將依次復制到節點,共創建兩個副本.
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
但這仍然容易出錯,就像在 case [1] 中顯式鍵入類型一樣.
But that is still error prone in the same way that explicitly typing the type in case [1].
到目前為止,我們有不同的調用 insert
的方法,需要在外部創建 value_type
并將該對象復制到容器中.或者,您可以使用 operator[]
如果類型是默認可構造 和 assignable(有意只關注 m[k]=v
),它需要對一個對象進行默認初始化,并將值復制到該對象中.
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.
在 C++11 中,通過可變模板和完美轉發,提供了一種通過 emplacing(就地創建)將元素添加到容器中的新方法.不同容器中的 emplace
函數做的事情基本上是一樣的:不是獲取 source 從中copy 到容器中,該函數需要將被轉發到存儲在容器中的對象的構造函數的參數.
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
在 [5] 中,std::pair
沒有被創建并傳遞給 emplace
,而是對 t 的引用
和 u
對象被傳遞給 emplace
,后者將它們轉發給數據結構內的 value_type
子對象的構造函數.在這種情況下,沒有 std::pair
的副本被完成,這是 emplace
的優勢C++03 替代品.與 insert
的情況一樣,它不會覆蓋地圖中的值.
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.
一個我沒有想到的有趣問題是 emplace
如何為地圖實際實現,這在一般情況下不是一個簡單的問題.
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.
這篇關于在 C++ 地圖中插入 vs emplace vs operator[]的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!