32

这样做的问题是巨大的物体将被复制到地图中

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});
4

4 回答 4

52

std::map::insert对 R 值有重载:

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

任何绑定到此重载的表达式都将调用 R 值构造函数。因为std::map<K,V>::value_typeis std::pair<const key_type, mapped_type>,并且std::pair有一个构造函数,它采用 R 值:

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

那么只要您使用创建 R 值的表达式插入该对,就可以保证在创建对象和插入映射时都会调用key_type和的 R 值构造函数,例如:mapped_typepair

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

或者

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

当然,所有这一切都依赖于Huge有一个合适的 R 值构造函数:

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


std::map::emplace最后,如果您只是想Huge在地图中构造一个新对象作为元素 ,您也可以使用。

于 2013-02-11T16:33:33.620 回答
16

你可以这样做({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);
于 2013-02-11T16:34:47.327 回答
6

避免复制和移动的替代方法是使用std::map::emplace(). 从链接的参考页面:

向容器中插入一个新元素。元素是就地构造的,即不执行复制或移动操作。元素类型(value_type,即std::pair)的构造函数被调用,参数与提供给函数的参数完全相同,用std::forward(args)转发。

于 2013-02-11T16:35:39.520 回答
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 值构造函数的类上工作,重量非常轻,内存所有权明确定义,并为您提供类似boost::optional<>的语义。可以使用std::unique_ptr比通过 R 值构造函数移动的对象更轻的参数,因为 R 值移动的对象需要分配(但公平地说,我使用的所有 C++11 编译器知道支持返回值优化或复制省略),即使对象的内脏被移动。

之所以这样std::unique_ptr<>工作是因为std::unique_ptr<>没有复制构造函数,它只有一个移动构造函数。

于 2013-02-19T09:34:51.770 回答