如何将unique_ptr捕获到lambda表达式中?

时间:2011-11-23 02:11:17

标签: c++ lambda c++11 unique-ptr

我尝试了以下内容:

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

但它没有编译。有什么想法吗?

更新:

AS建议,需要一些新语法来明确指定我们需要将所有权转移到lambda,我现在考虑以下语法:

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:

我将按以下方式显示movecopy的实施情况:

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 个答案:

答案 0 :(得分:52)

C ++ 14中的lambda generalized capture解决了这个问题:

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

答案 1 :(得分:31)

您无法永久捕获lambda中的unique_ptr。实际上,如果你想永久捕获lambda中的任何东西,它必须是 copyable ;只是可移动是不够的。

这可能被认为是C ++ 11中的一个缺陷,但您需要一些语法来明确表示您希望将unique_ptr值移动到lambda中。 C ++ 11规范的措辞非常谨慎,以防止对命名变量的隐式移动;这就是std::move存在的原因,这是的事情。

要执行您想要的操作,需要使用std::bind(这将是半复杂的,需要短序列binds)或只返回常规旧对象。

此外,永远不要将unique_ptr&&,除非您实际上正在编写其移动构造函数。只需按价值看待它;用户可以按值提供的唯一方法是使用std::move。实际上,除非你正在编写移动构造函数/赋值运算符(或实现转发函数),否则永远不要通过&&取任何东西。

答案 2 :(得分:18)

Nicol Bolas提到的使用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));
}

答案 3 :(得分:9)

对我有用的次优解决方案是将unique_ptr转换为shared_ptr,然后捕获lambda中的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();
    };
}

答案 4 :(得分:0)

我使用了这个非常狡猾的解决方法,其中涉及将unique_ptr粘贴在shared_ptr内。这是因为我的代码需要unique_ptr(由于API限制),所以我实际上无法将其转换为shared_ptr(否则我永远无法获得{ {1}}返回)。

我使用这种憎恶的理由是它适用于我的测试代码,我必须unique_ptr std::bind进入测试函数调用。

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在第一次调用期间已被移动/丢失。