【问题标题】:Moving an object into a map将对象移动到地图中
【发布时间】:2013-02-11 16:29: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});

【问题讨论】:

  • 我几天前就问过这个问题:stackoverflow.com/questions/14581414/…
  • 不完全一样,@Chowlett。
  • @Yakk - ...因为键类型也可用于移动,我需要确保它被复制到哪里?还是我错过了什么?
  • @Chowlett 是的——你想要部分移动,而 OP 只想移动。诚然,您的问题回答了 OP 的问题。 :)

标签: c++ c++11


【解决方案1】:

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

std::pair&lt;iterator,bool&gt; insert(value_type&amp;&amp;);

绑定到此重载的任何表达式都将调用 R 值构造函数。因为std::map&lt;K,V&gt;::value_typestd::pair&lt;const key_type, mapped_type&gt;,而std::pair 有一个接受R 值的构造函数:

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

那么,只要您使用创建 R 值的表达式,例如:

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

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

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

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


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

【讨论】:

  • 我还要补充一点,他使用大括号初始值设定项的示例也适用。此外,pair 构造函数实际上是一个通用引用构造函数,因为所有/任一/无参数都可能是 r 值,并且仍将被选择。称它为 r-value 构造函数可能会让人们误以为两个参数都必须是 r-value,这是不正确的。
【解决方案2】:

您可以这样做({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);

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      除了上述之外,您还可以依赖std::unique_ptr&lt;&gt; 缺少复制构造函数,尽管这会稍微改变接口。

      #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&lt;&gt;-like 语义。可以说std::unique_ptr 比通过 R 值构造函数移动的对象更轻,因为 R 值移动的对象需要分配(但公平地说,所有 C++11 编译器我知道支持返回值优化或复制省略),即使对象的内脏被移动了。

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

      【讨论】:

        猜你喜欢
        • 2017-02-02
        • 1970-01-01
        • 2017-06-26
        • 2016-11-15
        • 2013-07-07
        • 1970-01-01
        • 2019-11-02
        • 1970-01-01
        相关资源
        最近更新 更多