2013-02-11 191 views
25

的問題,這是一個巨大的物體將被複制到地圖移動對象到地圖

Huge huge1(some,args); 
Huge huge2(some,args); 

std::map<int,Huge> map1; 
std::map<Huge,int> map2; 

map1.insert({0,huge1}); 
map2.insert({huge2,0}); 

我怎麼能保證一招?這工作還是有更多的?

map1.insert({0,std::move(huge1)}); 
map2.insert({std::move(huge2),0}); 
+1

我幾天前就問過這個:http://stackoverflow.com/questions/14581414/insert-map-entry-by-r-value-moving-of-mapped-type – Chowlett 2013-02-11 16:33:29

+0

不完全一樣的東西, @Chowlett。 – Yakk 2013-02-11 16:48:06

+0

@Yakk - ...因爲鑰匙類型也可以移動,我需要確保它被複制?還是我錯過了別的? – Chowlett 2013-02-11 16:59:36

回答

37

std::map::insert具有用於R值的過載:

std::pair<iterator,bool> insert(value_type&&);

其結合此重載將調用R-值構造任何表達式。由於std::map<K,V>::value_typestd::pair<const key_type, mapped_type>,並std::pair有一個構造函數的R值:

template<class U1, class U2> 
pair(U1&& x, U2&& y); 

那麼你就保證爲key_typemapped_type R值的構造函數將被調用,無論是在創建pair對象,並在地圖插入,只要使用創建的R值,如表達插入一對:

map1.insert(std::make_pair(0, Huge()); 

OR

map1.insert(std::make_pair(0, std::move(huge1)); 

當然,所有這一切都依賴於有一個正確的R值構造Huge

Huge(Huge&& h) 
{ 
    ... 
} 


最後,您還可以使用std::map::emplace,如果你只是想建立一個新的Huge對象作爲元素在地圖中。

+1

我還會補充說他使用的大括號初始值設定項的例子也適用。另外,pair構造函數實際上是一個通用引用構造函數,因爲所有/任何/沒有參數可能是r值,它仍然會被選中。稱它爲r值構造函數可能會讓人們誤以爲這兩個參數都必須是r值,這是不正確的。 – mmocny 2013-02-19 14:27:12

14

你可以這樣做({0,std::move(huge1)}部分)。但你也可以跳過中間商(假設你構造函數中的對象)是這樣的:

map1.emplace(std::piecewise_construct, 0, std::forward_as_tuple(some, args)); 
map2.emplace(std::piecewise_construct, std::forward_as_tuple(some, args), 0); 

或者,如果你的函數給出的對象,你仍然可以使用emplace

map1.emplace(0, std::move(huge1)); 
map2.emplace(std::move(huge1), 0); 
6

避免複製和移動的替代方法是使用std::map::emplace()。從鏈接的參考頁面:

將新元素插入容器。 元素是在原地構建的,即不執行復制或移動操作。元素類型(VALUE_TYPE,即,標準::對)被調用,實際提供的完全相同的功能,與標準::向前(參數)轉發相同的參數的構造....

2

除此之外,還可以依賴std::unique_ptr<>缺少拷貝構造函數,儘管這稍微改變了接口。

#include <iostream> 
#include <map> 
#include <memory> 

class Huge { 
public: 
    Huge(int i) : x{i} {} 
    int x; 
}; 

using HugePtrT = std::unique_ptr<Huge>; 
using MyMapT = std::map<int, HugePtrT>; 


int 
main() { 
    MyMapT myMap; 
    myMap[42].reset(new Huge{1}); 
    std::cout << myMap[42]->x << std::endl; 
    myMap[43] = std::move(myMap[42]); 
    if (myMap[42]) 
    std::cout << "42: " << myMap[42]->x << std::endl; 
    if (myMap[43]) 
    std::cout << "43: " << myMap[43]->x << std::endl; 
} 

產生預期的輸出:

1 
43: 1 

如果省略了std::move()呼叫時,程序將無法編譯。同樣,您可以使用.reset()來分配指針。

這樣做的好處是它可以處理沒有R值構造函數的類,它的權重非常輕,內存所有權被明確定義,併爲您提供類似於語義的語義。由於R值移動的對象需要分配(儘管公平起見,我所有的C++ 11編譯器都是這樣的,所以我們可以指出std::unique_ptr比通過R值構造函數移動的對象更輕, m知道支持返回值優化或複製elision),即使對象的內臟被移動。

原因std::unique_ptr<>這樣工作是因爲std::unique_ptr<>沒有複製構造函數,它只有一個移動構造函數。