Перемещение вставленного элемента контейнера, если это возможно - PullRequest
3 голосов
/ 24 апреля 2010

Я пытаюсь добиться следующей оптимизации в моей библиотеке контейнеров:

  • при вставке элемента со ссылкой на lvalue скопируйте его во внутреннее хранилище;
  • но при вставке элемента с rvalue-ссылкой переместить , если поддерживается.

Предполагается, что оптимизация будет полезной, например, если тип содержащегося элемента - что-то вроде 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 ]

2 голосов
/ 24 апреля 2010

Я думаю, вам, возможно, понадобится перевести аргумент:

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

Ключевое слово, которое вы можете искать, - "совершенная пересылка".

0 голосов
/ 24 апреля 2010

Не могу сказать, что понимаю, почему это работает, а какой-то другой код - нет, но, похоже, это помогает (создается благодаря подсказкам из 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
0 голосов
/ 24 апреля 2010

Я бы порекомендовал скопировать способ, которым это делается в вашей реализации 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 ());
  }
}
...