【问题标题】:Moving inserted container element if possible如果可能,移动插入的容器元素
【发布时间】:2010-04-24 14:01:28
【问题描述】:

我正在尝试在我的容器库中实现以下优化:

  • 插入左值引用元素时,将其复制到内部存储;
  • 但在插入 rvalue-referenced 元素时,如果支持,移动它。

优化应该是有用的,例如如果包含的元素类型类似于std::vector,则在可能的情况下移动将大大加快速度。

但是,到目前为止,我无法为此设计任何工作方案。我的容器相当复杂,所以我不能多次重复insert() 代码:它很大。我想将所有“真实”代码保留在某个内部助手中,比如do_insert()(可能是模板化的),而各种类似insert() 的函数只会用不同的参数调用它。

我最好的代码(当然是原型,没有做任何实际的事情):

#include <iostream>
#include <utility>

struct element
{
  element () { };
  element (element&&) { std::cerr << "moving\n"; }
};

struct container
{
  void  insert (const element& value)
  {  do_insert (value);  }

  void  insert (element&& value)
  {  do_insert (std::move (value));  }

private:
  template <typename Arg>
  void  do_insert (Arg arg)
  {  element  x (arg);  }
};

int
main ()
{
  {
    // Shouldn't move.
    container  c;
    element x;
    c.insert (x);
  }

  {
    // Should move.
    container  c;
    c.insert (element ());
  }
}

但是,这至少不适用于 GCC 4.4 和 4.5:它永远不会在 stderr 上打印“移动”。或者是我想要的不可能实现,这就是为什么emplace()-like 函数首先存在?

【问题讨论】:

    标签: c++ c++11 move-constructor


    【解决方案1】:

    我认为您可能需要转发参数:

      template <typename Arg>
      void  do_insert (Arg&& arg)
      {  element  x (std::forward<Arg>(arg));  }
    

    完整代码:

    #include <iostream>
    #include <utility>
    
    struct element
    {
      element () { };
      element (const element&) { std::cerr << "copying\n"; }
      element (element&&) { std::cerr << "moving\n"; }
    };
    
    struct container
    {
      void  insert (const element& value)
      {  do_insert (value);  }
    
      void  insert (element&& value)
      {  do_insert (std::move(value));  }
    
    private:
      template <typename Arg>
      void  do_insert (Arg&& arg)
      {  element  x (std::forward<Arg>(arg));  }
    };
    
    int
    main ()
    {
      {
        // Shouldn't move.
        container  c;
        element x;
        c.insert (x);
      }
      {
        // Should move.
        container  c;
        c.insert (element ());
      }
    }
    

    您可能会寻找的关键字是“完美转发”。

    【讨论】:

    • 如果我这样做,我会得到两个“移动”的输出。但我想我需要阅读有关该功能的一些文档。
    • 编辑后的代码可以正常工作,并且缺少do_insert() 的奇怪的显式 参数,这与我的自我回答不同。谢谢!
    【解决方案2】:

    我建议复制它在您的 STL 实现(或 GNU,无论如何都应该能够在线阅读)中完成的方式。

    但是

      template <typename Arg>
      void  do_insert (Arg arg)
      {  element  x (move(arg));  }
    

    可能会成功。

    此功能与emplace 是分开的,您说它在标准库中工作是正确的。

    编辑我做了一些更改并发现

    • 当您收到两条消息时,每次插入都有一条消息
    • 第二次插入似乎没问题
    • 第一个生成一个虚假的move,但原始对象没有移动。所以专注于寻找一个正在被移动的临时......这实际上并不是一件坏事。

    .

    #include <iostream>
    #include <utility>
    
    struct element
    {
      element () : moved(false) { };
      element (element&&) { moved = true; std::cerr << "moving\n"; }
      bool moved;
    };
    
    struct container
    {
      void  insert (const element& value)
      {  do_insert (value);  }
    
      void  insert (element&& value)
      {  do_insert (std::move (value));  }
    
    private:
      template <typename Arg>
      void  do_insert (Arg arg)
      {  element  x (std::move(arg));  }
    };
    
    int
    main ()
    {
      std::cerr << "try 1\n";
      {
        // Shouldn't move.
        container  c;
        element x;
        c.insert (x);
        std::cerr << x.moved << "\n";
      }
    
      std::cerr << "try 2\n";
      {
        // Should move.
        container  c;
        c.insert (element ());
      }
    }
    

    【讨论】:

    • 在 GNU STL 中没有为类似的 containerd (unordered_*) 实现。这可能表明它不可能(容易地)做到,或者它可能只是还没有实现。关于转发不动:也许,但也无济于事。
    • @doublep:我的意思其实是你需要修改do_insert。 GNU(可能还有其他所有人)使用分配器处理此步骤,因此哪个容器无关紧要。我不能 100% 确定是使用 move 还是 forward,但是您的 do_insert 中缺少任何一个显然是一个错误。
    • @Potatoswatter:无论我在do_insert() 中添加什么,我都会收到两条“动人”的消息。 IE。这很可能是一个错误,但我不知道如何解决它。
    • 当我运行这个更新的代码时,在这两种情况下我都会收到“正在移动”的消息。另外,我认为moved 标志是错误的,因为您在打印x.moved 时所做的是(逻辑上)检查“x 已被移走”,而不是“x 是使用移动构造函数创建的”。跨度>
    • @doublep:我们使用移动构造函数创建的唯一东西是本地的。对于我打印的诊断,这就是我正在寻找的。我们要确保x 没有被移走。
    【解决方案3】:

    我不能说我理解为什么这有效而其他一些代码无效,但这似乎可以解决问题(由于 Potatoswatter 的提示而创建):

    #include <iostream>
    #include <utility>
    
    struct element
    {
      element () { };
      element (const element&) { std::cerr << "copying\n"; }
      element (element&&) { std::cerr << "moving\n"; }
    };
    
    struct container
    {
      void  insert (const element& value)
      {  do_insert <const element&> (value);  }
    
      void  insert (element&& value)
      {  do_insert <element&&> (std::forward <element&&> (value));  }
    
    private:
      template <typename Arg>
      void  do_insert (Arg arg)
      {  element  x (std::forward <Arg> (arg));  }
    };
    
    int
    main ()
    {
      std::cerr << "1\n";
      {
        // Shouldn't move.
        container  c;
        element x;
        c.insert (x);
      }
    
      std::cerr << "2\n";
      {
        // Should move.
        container  c;
        c.insert (element ());
      }
    }
    

    我在 GCC 4.4 和 4.5 中都得到以下输出:

    1
    copying
    2
    moving
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-29
      • 2021-09-12
      相关资源
      最近更新 更多