Подтвердить что ты не робот

Перемещение объекта в карту

Проблема заключается в том, что огромные объекты будут скопированы в карты

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});
4b9b3361

Ответ 1

std::map::insert имеет перегрузку для значений R:

std::pair<iterator,bool> insert(value_type&&);

Любое выражение, которое связывается с этой перегрузкой, вызывается конструкторами R-значения. Поскольку std::map<K,V>::value_type есть std::pair<const key_type, mapped_type>, а std::pair имеет конструктор, который принимает R-значения:

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

тогда вам гарантируется, что конструкторы R-value для key_type и mapped_type будут вызваны как при создании объекта pair, так и при вставке карты, если вы вставляете пару, используя выражение, которое создает значения R, такие как:

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

ИЛИ

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

Конечно, все это зависит от Huge наличия правильного конструктора R-значения:

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


Наконец, вы также можете использовать std::map::emplace, если хотите просто создать новый объект Huge как элемент на карте.

Ответ 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<> отсутствие конструктора копирования, хотя это немного меняет интерфейс.

#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<> -подобную семантику. Можно было бы привести аргумент, что std::unique_ptr является более легким весом, чем объект, который был перемещен с помощью конструктора R-значения, потому что объект, перемещенный по R-значению, требует распределения (хотя, быть честным, все компиляторы С++ 11, которые Я знаю поддержку Оптимизация возвращаемого значения или копирование elision), хотя перемещаются кишки объекта.

Причина std::unique_ptr<> работает так, потому что std::unique_ptr<> не имеет конструктора копирования, он имеет только конструктор перемещения.