尽可能移动插入的容器元素

时间:2010-04-24 14:01:28

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

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

  • 插入左值引用的元素时,将其复制到内部存储;
  • 但是在插入 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() - 首先存在类似函数的原因?

3 个答案:

答案 0 :(得分:2)

我认为你可能需要提出这个论点:

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

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

答案 1 :(得分:0)

我建议复制它在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 ());
  }
}

答案 2 :(得分:0)

我不能说我理解为什么这会有效,而其他一些代码却没有,但这似乎可以解决问题(感谢来自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