如果可能的话移动插入的容器元素

发布于 2024-08-29 23:05:07 字数 1127 浏览 7 评论 0原文

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

  • 插入左值引用元素时,将其复制到内部存储;
  • 但是当插入右值引用元素时,移动它(如果支持)。

优化应该是有用的,例如,如果包含的元素类型类似于 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() 那样的函数首先存在的原因?

I'm trying to achieve the following optimization in my container library:

  • when inserting an lvalue-referenced element, copy it to internal storage;
  • but when inserting rvalue-referenced element, move it if supported.

The optimization is supposed to be useful e.g. if contained element type is something like std::vector, where moving if possible would give substantial speedup.

However, so far I was unable to devise any working scheme for this. My container is quite complicated, so I can't just duplicate insert() code several times: it is large. I want to keep all "real" code in some inner helper, say do_insert() (may be templated) and various insert()-like functions would just call that with different arguments.

My best bet code for this (a prototype, of course, without doing anything real):

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

However, this doesn't work at least with GCC 4.4 and 4.5: it never prints "moving" on stderr. Or is what I want impossible to achieve and that's why emplace()-like functions exist in the first place?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浮生未歇 2024-09-05 23:05:07

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

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

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

I think you may need to forward the argument:

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

Full code:

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

The keyword that you might look for is "perfect forwarding".

贵在坚持 2024-09-05 23:05:07

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

I'd recommend to copy the way it's done in your STL implementation (or GNU, which should be able to read online anyway).

But

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

might do the trick.

This functionality is separate from emplace and you're correct that it works in the standard library.

EDIT I made some changes and discovered

  • When you get two messages, there is one from each insertion
  • The second insertion appears to be fine
  • The first generates a spurious move but the original object was not moved. So focus on finding a temporary which is being moved… this isn't actually such a bad thing.

.

#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 ());
  }
}
忆沫 2024-09-05 23:05:07

我不能说我明白为什么这个有效,而其他一些代码却不起作用,但这似乎可以解决问题(感谢 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

I can't say I understand why this works and some other code doesn't, but this seems to do the trick (created thanks to hints from 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 ());
  }
}

I get the following output with both GCC 4.4 and 4.5:

1
copying
2
moving
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文