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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8236521/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:14:07  来源:igfitidea点击:

How to capture a unique_ptr into a lambda expression?

c++lambdac++11unique-ptr

提问by Earth Engine

I have tried the following:

我尝试了以下方法:

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

But it does not compile. Any ideas?

但它不编译。有任何想法吗?

UPDATE:

更新:

AS suggested, some new syntax is required to explicitly specify we need to transfer the ownership to the lambda, I am now thinking about the following syntax:

正如建议的那样,需要一些新语法来明确指定我们需要将所有权转移到 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
    };
}

Would it be a good candidate?

它会是一个很好的候选人吗?

UPDATE 1:

更新1:

I will show my implementation of moveand copyas following:

我将展示我的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.
}

回答by mattnewport

This issue is addressed by lambda generalized capturein C++14:

此问题已通过C++14 中的lambda 广义捕获解决:

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

回答by Nicol Bolas

You cannot permanently capture a unique_ptrin a lambda. Indeed, if you want to permanently capture anything in a lambda, it must be copyable; merely movable is insufficient.

您不能unique_ptr在 lambda 中永久捕获 a 。事实上,如果你想在 lambda 中永久捕获任何东西,它必须是可复制的;仅仅移动是不够的。

This could be considered a defect in C++11, but you would need some syntax to explicitly say that you wanted to move the unique_ptrvalue into the lambda. The C++11 specification is very carefully worded to prevent implicit moves on named variables; that's why std::moveexists, and this is a goodthing.

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

To do what you want will require either using std::bind(which would be semi-convoluted, requiring a short sequence of binds) or just returning a regular old object.

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

Also, never take unique_ptrby &&, unless you are actually writing its move constructor. Just take it by value; the only way a user can provide it by value is with a std::move. Indeed, it's generally a good idea to never take anything by &&, unless you're writing the move constructor/assignment operator (or implementing a forwarding function).

另外,永远不要 take unique_ptrby &&,除非您实际上是在编写它的移动构造函数。只需按价值取即可;用户可以按值提供它的唯一方法是使用std::move. 实际上,&&除非您正在编写移动构造函数/赋值运算符(或实现转发函数),否则永远不要使用任何东西通常是个好主意。

回答by marton78

The "semi-convoluted" solution using std::bindas mentioned by Nicol Bolas is not so bad after all:

使用std::bindNicol Bolas 提到的“半复杂”解决方案毕竟还不错:

std::function<void ()> getAction(std::unique_ptr<MyClass>&& psomething)
{
    return std::bind([] (std::unique_ptr<MyClass>& p) { p->do_some_thing(); },
                     std::move(psomething));
}

回答by tcb

A sub-optimal solution that worked for me was to convert the unique_ptrto a shared_ptrand then capture the shared_ptrin the lambda.

对我有用的次优解决方案是将 the 转换unique_ptr为 ashared_ptr然后shared_ptr在 lambda 中捕获 the 。

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

回答by Malvineous

I used this really dodgy workaround, which involves sticking the unique_ptrinside a shared_ptr. This is because my code required a unique_ptr(due to an API restriction) so I couldn't actually convert it to a shared_ptr(otherwise I'd never be able to get my unique_ptrback).

我用这个真的狡猾的解决办法,其中包括坚持的unique_ptr内部shared_ptr。这是因为我的代码需要 a unique_ptr(由于 API 限制),所以我实际上无法将其转换为 a shared_ptr(否则我永远无法unique_ptr恢复)。

My justification for using this abomination is that it was for my test code, and I had to std::binda unique_ptrinto the test function call.

我使用这种可憎的理由是,这是我的测试代码,我不得不std::bindunique_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);
});

Now calling fnTest()will call run_test()while passing the unique_ptrto it. Calling fnTest()a second time will result in an assertion failure, because the unique_ptrhas already been moved/lost during the first call.

现在调用fnTest()run_test()在传递unique_ptr给它时调用。fnTest()第二次调用将导致断言失败,因为unique_ptr在第一次调用期间已经移动/丢失了。