Как записать unique_ptr в лямбда-выражение? - PullRequest
37 голосов
/ 23 ноября 2011

Я пробовал следующее:

std::function<void ()> getAction(std::unique_ptr<MyClass> &&psomething){
    //The caller given ownership of psomething
    return [psomething](){ 
        psomething->do_some_thing();
        //psomething is expected to be released after this point
    };
}

Но он не компилируется. Есть идеи?

UPDATE:

Как и предполагалось, требуется какой-то новый синтаксис, чтобы явно указать, что нам нужно передать владение лямбде, сейчас я думаю о следующем синтаксисе:

std::function<void ()> getAction(std::unique_ptr<MyClass> psomething){
    //The caller given ownership of psomething
    return [auto psomething=move(psomething)](){ 
        psomething->do_some_thing();
        //psomething is expected to be released after this point
    };
}

Это был бы хороший кандидат?

ОБНОВЛЕНИЕ 1:

Я покажу мою реализацию move и copy следующим образом:

template<typename T>
T copy(const T &t) {
    return t;
}

//process lvalue references
template<typename T>
T move(T &t) {
    return std::move(t);
}

class A{/*...*/};

void test(A &&a);

int main(int, char **){
    A a;
    test(copy(a));    //OK, copied
    test(move(a));    //OK, moved
    test(A());        //OK, temporary object
    test(copy(A()));  //OK, copying temporary object
    //You can disable this behavior by letting copy accepts T &  
    //test(move(A())); You should never move a temporary object
    //It is not good to have a rvalue version of move.
    //test(a); forbidden, you have to say weather you want to copy or move
    //from a lvalue reference.
}

Ответы [ 5 ]

55 голосов
/ 06 июня 2013

Эта проблема решена с помощью лямбда-обобщенного захвата в C ++ 14:

// a unique_ptr is move-only
auto u = make_unique<some_type>(some, parameters); 

// move the unique_ptr into the lambda
go.run([u = move(u)]{do_something_with(u);});
31 голосов
/ 23 ноября 2011

Вы не можете навсегда захватить unique_ptr в лямбду.Действительно, если вы хотите постоянно захватывать что-либо в лямбду, это должно быть copyable ;недостаточно просто переместить.

Это может считаться дефектом в C ++ 11, но вам потребуется некоторый синтаксис, чтобы явно сказать, что вы хотите переместить значение unique_ptr в лямбду.Спецификация C ++ 11 очень тщательно сформулирована, чтобы предотвратить неявные перемещения именованных переменных;вот почему существует std::move, а это хорошо .

Чтобы сделать то, что вы хотите, потребуется либо использовать std::bind (что будет полу-извилистым, требующим короткой последовательностииз binds) или просто возвращая обычный старый объект.

Кроме того, никогда не принимайте unique_ptr от &&, если вы на самом деле не пишете его конструктор перемещения.Просто возьмите это по значению;единственный способ, которым пользователь может предоставить его по значению, это std::move.На самом деле, это хорошая идея - никогда не брать что-либо по &&, если вы не пишете конструктор перемещения / оператор присваивания (или не реализуете функцию пересылки).

18 голосов
/ 05 октября 2012

«Полу извилистое» решение с использованием std::bind, как упомянул Никол Болас, не так уж и плохо:

std::function<void ()> getAction(std::unique_ptr<MyClass>&& psomething)
{
    return std::bind([] (std::unique_ptr<MyClass>& p) { p->do_some_thing(); },
                     std::move(psomething));
}
9 голосов
/ 13 января 2015

Неоптимальным решением, которое мне помогло, было преобразование unique_ptr в shared_ptr, а затем захват shared_ptr в лямбде.

std::function<void()> getAction(std::unique_ptr<MyClass> psomething)
{
    //The caller given ownership of psomething
    std::shared_ptr<MyClass> psomethingShared = std::shared_ptr<MyClass>(std::move(psomething));
    return [psomethingShared]()
    {
        psomethingShared->do_some_thing();
    };
}
1 голос
/ 02 апреля 2015

Я использовал этот действительно хитрый обходной путь, который включает в себя вставку unique_ptr внутри shared_ptr. Это связано с тем, что мой код требовал unique_ptr (из-за ограничения API), поэтому я не мог на самом деле преобразовать его в shared_ptr (иначе я бы никогда не смог вернуть unique_ptr).

Мое оправдание для использования этой мерзости в том, что это было для моего тестового кода, и мне пришлось std::bind a unique_ptr в вызов функции теста.

// Put unique_ptr inside a shared_ptr
auto sh = std::make_shared<std::unique_ptr<Type>>(std::move(unique));

std::function<void()> fnTest = std::bind([this, sh, input, output]() {
    // Move unique_ptr back out of shared_ptr
    auto unique = std::move(*sh.get());

    // Make sure unique_ptr is still valid
    assert(unique);

    // Move unique_ptr over to final function while calling it
    this->run_test(std::move(unique), input, output);
});

Теперь вызов fnTest() вызовет run_test() при передаче ему unique_ptr. Повторный вызов fnTest() приведет к ошибке подтверждения, так как unique_ptr уже был перемещен / потерян во время первого вызова.

...