简体   繁体   English

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

[英]How to capture a unique_ptr into a lambda expression?

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 move and copy as following:我将展示我的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.
}

This issue is addressed by lambda generalized capture in 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);});

You cannot permanently capture a unique_ptr in a lambda. 您无法永久捕获lambda中的unique_ptr Indeed, if you want to permanently capture anything in a lambda, it must be copyable ; 实际上,如果你想永久捕获lambda中的任何东西,它必须是可复制的 ; merely movable is insufficient. 只是可移动是不够的。

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_ptr value into the lambda. 这可能被认为是C ++ 11中的一个缺陷,但是你需要一些语法来明确地说你想将unique_ptr值移动到lambda中。 The C++11 specification is very carefully worded to prevent implicit moves on named variables; C ++ 11规范的措辞非常谨慎,以防止对命名变量的隐式移动; that's why std::move exists, and this is a good thing. 这就是为什么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_ptr by && , unless you are actually writing its move constructor. 此外,永远不要使用&&获取unique_ptr ,除非您实际上正在编写其移动构造函数。 Just take it by value; 只需按价值看待它; the only way a user can provide it by value is with a std::move . 用户可以通过值提供它的唯一方法是使用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). 实际上,除非你正在编写移动构造函数/赋值运算符(或实现转发函数),否则永远不要通过&&取任何东西。

The "semi-convoluted" solution using std::bind as mentioned by Nicol Bolas is not so bad after all: 使用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));
}

A sub-optimal solution that worked for me was to convert the unique_ptr to a shared_ptr and then capture the shared_ptr in the lambda. 对我unique_ptr的次优解决方案是将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();
    };
}

I used this really dodgy workaround, which involves sticking the unique_ptr inside a shared_ptr . 我使用了这个非常狡猾的解决方法,其中涉及将unique_ptr粘贴在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_ptr back). 这是因为我的代码需要unique_ptr (由于API限制),所以我实际上无法将其转换为shared_ptr (否则我永远无法将我的unique_ptr恢复)。

My justification for using this abomination is that it was for my test code, and I had to std::bind a unique_ptr into the test function call. 我使用这种憎恶的理由是它适用于我的测试代码,我必须std::bind一个unique_ptr std::bind到测试函数调用中。

// 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_ptr to it. 现在调用fnTest()将调用run_test()同时将unique_ptr传递给它。 Calling fnTest() a second time will result in an assertion failure, because the unique_ptr has already been moved/lost during the first call. fnTest()调用fnTest()将导致断言失败,因为unique_ptr在第一次调用期间已被移动/丢失。

One also need to know, that lambdas capturing unique_ptr cannot be converted into std::function because std::function requires that the callable object is copyable.还需要知道,捕获 unique_ptr 的 lambda不能转换为 std::function,因为std::function要求可调用的 object 是可复制的。

auto lambdaWithoutCapture = [](){return 1;}; //Can be std::function
auto lambdaWithCapture = [=](){return 1;}; //Can be std::function
auto lambdaWithCapture2 = [&](){return 1;}; //Can be std::function
auto lambdaWithCapture3 = [uptrProblematic = std::move(uptrProblematic)]() mutable {return 1;}; //Can't be std::function

Therefore, if you don't have to specify return type of the function, you can use such approach which does not use std::function.因此,如果您不必指定 function 的返回类型,则可以使用不使用 std::function 的方法。 But you need to know, that this will only work in local scope. You can't declare auto workerFactory();但是你需要知道,这只适用于本地 scope。你不能声明auto workerFactory(); in header file, as this will raise compilation error.在 header 文件中,因为这会引发编译错误。

auto workerFactory()
{
    std::unique_ptr uptrProblematic = std::make_unique<int>(9);
    int importantData = 71;
    return [=, uptrProblematic = std::move(uptrProblematic)](std::string input) mutable -> int {
        std::cout << "Problematic variable is equal to: " << *uptrProblematic << "\n";
        std::cout << "Important data is equal to: " << importantData << "\n";
        std::cout << "Input equal to: " << input << "\n";
        return 9;
    };
}

int main()
{
    auto worker = workerFactory();
    worker("Test");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM