Как захватить std:: unique_ptr "путем перемещения" для лямбда в std:: for_each - программирование
Подтвердить что ты не робот

Как захватить std:: unique_ptr "путем перемещения" для лямбда в std:: for_each

Я изучаю новые функции в С++ 11 и сталкивался с этой проблемой. Я хотел бы захватить unique_ptr, перемещая его внутри лямбды в качестве аргумента for_each.

настроить:

std::array<int,4> arr = {1,3,5,6};
std::unique_ptr<int> p(new int);  (*p) = 3;

попытка 1 - не работает, потому что unique_ptr не имеет конструктора копирования. С++ 0x не указывает синтаксис pass by move.

std::for_each(arr.begin(), arr.end(), [p](int& i) { i+=*p; });

попытка 2 - использовать bind для привязки перенесенной копии p к функции, которая принимает int &:

std::for_each(arr.begin(), arr.end(),
     std::bind([](const unique_ptr<int>& p, int& i){
          i += (*p);
     }, std::move(p))
);

Компилятор жалуется, что 'result' : symbol is neither a class template nor a function template.

Основная цель упражнения - понять, как перемещать подвижную переменную в лямбда, которая кэшируется для дальнейшего использования.

4b9b3361

Ответ 1

Обновление: вы можете захватить подвижную переменную в lambda с С++ 14 и далее.

std::for_each(arr.begin(), arr.end(), [p=std::move(p)](int& i) { i+=*p; });

Вы не можете зафиксировать подвижную переменную в лямбда любым простым способом в С++ 11.

Захват Lambdas копией или ссылкой. Таким образом, чтобы зафиксировать переменную только для перемещения, вы должны обернуть ее в объект, где копирование = > перемещение (например, std::auto_ptr). Это неприятный хак.

В вашем примере вы можете просто захватить по ссылке, но если это был просто упрощенный код, он может не делать то, что вы хотели с реальным кодом:

std::for_each(arr.begin(), arr.end(), [&p](int& i) { i+=*p; });

Здесь оболочка copy-move-only:

template<typename T>
struct move_on_copy_wrapper
{
    mutable T value;

    move_on_copy_wrapper(T&& t):
        value(std::move(t))
    {}

    move_on_copy_wrapper(move_on_copy_wrapper const& other):
        value(std::move(other.value))
    {}

    move_on_copy_wrapper(move_on_copy_wrapper&& other):
        value(std::move(other.value))
    {}

    move_on_copy_wrapper& operator=(move_on_copy_wrapper const& other)
    {
        value=std::move(other.value);
        return *this;
    }

    move_on_copy_wrapper& operator=(move_on_copy_wrapper&& other)
    {
        value=std::move(other.value);
        return *this;
    }

};

Затем вы можете использовать его следующим образом:

int main()
{
    std::unique_ptr<int> p(new int(3));
    move_on_copy_wrapper<std::unique_ptr<int>> mp(std::move(p));

    [mp]()
    {
        std::cout<<"*mp.value="<<*mp.value<<std::endl;
    }
    ();

    std::cout<<"p="<<p.get()<<", mp="<<mp.value.get()<<std::endl;
}

Ответ 2

Ваша попытка 2 почти сработает.

Что вам не хватает, вы не сказали своему bind вызову ожидать параметр:

std::for_each(arr.begin(), arr.end(),
   std::bind([](const unique_ptr<int>& p, int& i){
      i += (*p);
   }, std::move(p), std::placeholders::_1)
);

placeholders::_1 необходимо сообщить результат bind, что он должен ожидать переданный ему параметр для operator().

Это также предложение, данное в @marton78 answer здесь.