2013-02-11 81 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<>没有复制构造函数,它只有一个移动构造函数。