Как перехватить unique_ptr в std :: function - PullRequest
1 голос
/ 08 июля 2019

Мне нужно переместить unique_ptr в std::function замыкание. Я использую обобщенные лямбда-захваты в C ++ 14.

auto ptr = make_unique<Foo>();

// Works.
auto lambda = [p = move(ptr)] { };

// This does not compile.
std::function<void()> func = [p = move(ptr)] { };

Он пытается скопировать, а не переместить лямбда-захват в std::function. Соответствующая ошибка:

 copy constructor of '' is implicitly deleted because field '' has a deleted copy
      constructor
  std::function<void()> func = [p = move(ptr)] { };

Пример здесь может показаться, что это работает.

Обратите внимание, что ответ здесь просто повторяет пример на isocpp.org.

Я могу перейти к shared_ptr следующим образом:

shared_ptr<Foo> s = move(ptr);

но это создает другую проблему, потому что мне нужно вызвать функцию, которая ожидает unique_ptr изнутри моей лямбды, и я не могу преобразовать shared_ptr обратно в unique_ptr.

Можно ли захватить unique_ptr за std::function?

Ответы [ 2 ]

1 голос
/ 08 июля 2019

Решение было довольно простым.Вместо перемещения unique_ptr в shared_ptr используйте shared_ptr для указания на unique_ptr:

auto holder = make_shared<unique_ptr<Foo>>(move(ptr));
std::function<void()> func = [holder] { MyFunction(move(*holder)); };

Примечание: в моем случае использования func вызывается только один раз, поэтомуmove(*holder) в порядке.

0 голосов
/ 08 июля 2019

std :: function объекты могут быть скопированы

std::function - это объект стирания типа, который поддерживает копирование сохраненного объекта.

Когда вы храните std::unique_ptr в лямбде, эта лямбда не поддерживает копирование.

Так что std::function вполне справедливо жалуется. Это тип, который можно скопировать, и когда передается что-то, выясняется, как его скопировать. «Я не могу скопировать это» не является правильным ответом; все std::function s могут быть скопированы.

Промышленное решение для прочности:

Существует два распространенных подхода к решению этой проблемы. Во-первых, вы сохраняете состояние std::function в некотором виде std::shared_ptr. Во-вторых, вы пишете или находите не копирующий std::function и используете его вместо этого.

Более "современные" std::function библиотеки замены поддерживают ряд полезных вещей:

  1. представления функций, которым не принадлежит то, что они переносят.
  2. Функциональные объекты только для перемещения, которые не поддерживают копирование.
  3. Объекты с множественной перегрузкой, которые поддерживают более 1 подписи одновременно.
  4. буферы фиксированного размера, которые не компилируются, если не хватает автоматического хранилища вместо выделения кучи.
  5. тривиально копируемые функциональные объекты

Я лично нуждался в каждом из вышеперечисленных для различных специальных целей.

Тогда вы будете использовать moveonly_function<void()>, когда вам не нужно копировать вызываемый объект, и ваш код компилируется.

Однако сейчас это, вероятно, слишком тяжело для ваших нужд.

Быстрое решение:

template<class F>
auto make_shared_function( F&& f ) {
  return
   [pf = std::make_shared<std::decay_t<F>>(std::forward<F>(f))]
   (auto&&...args)->decltype(auto)
   {
     return (*pf)( decltype(args)(args)... );
   };
}

теперь, когда вы сталкиваетесь с этой проблемой:

// This does not compile.
std::function<void()> func = make_shared_function([p = move(ptr)] { });

и состояние вызываемого объекта теперь сохраняется в общем ptr.

...