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

t5zmwmid  于 2022-12-30  发布在  其他
关注(0)|答案(6)|浏览(173)

我尝试了以下方法:

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

但它不能编译。有什么想法吗?
更新:
正如建议的那样,需要一些新的语法来显式指定我们需要将所有权转移到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.
}
bjp0bcyl

bjp0bcyl1#

这个问题由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);});
zphenhs4

zphenhs42#

你不能在lambda中永久捕获unique_ptr,事实上,如果你想在lambda中永久捕获任何东西,它必须是 copyable 的;仅仅可移动是不够的。
这可能被认为是C11中的一个缺陷,但是你需要一些语法来明确地表示你想把unique_ptr值移到lambda中。C11规范的措辞非常谨慎,以防止对命名变量的隐式移动;这就是std::move存在的原因,这是一件“好”事情。
要完成所需的操作,需要使用std::bind(这将是半卷积的,需要一个binds的短序列)或只返回一个常规的旧对象。
另外,不要逐个取unique_ptr,除非你实际上是在写它的移动构造函数。用户可以通过值提供它的唯一方法是使用std::move。实际上,通常最好不要使用&&,除非您正在编写移动构造函数/赋值运算符(或实现转发函数)。

kninwzqo

kninwzqo3#

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

eblbsuwk4#

对我来说,一个次优的解决方案是将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();
    };
}
6ojccjat

6ojccjat5#

我使用了一个非常狡猾的解决方法,将unique_ptr粘贴到shared_ptr中,这是因为我的代码需要unique_ptr(由于API限制),所以我无法将其转换为shared_ptr(否则我将永远无法恢复unique_ptr)。
我之所以使用这个令人厌恶的代码,是因为它是用于测试代码的,我必须将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);
});

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

30byixjq

30byixjq6#

我们还需要知道,捕获unique_ptr的lambda不能转换为std::function,因为std::function要求可调用对象是可复制的。

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

因此,如果你不需要指定函数的返回类型,你可以使用不使用std::function的方法。但是你需要知道,这只在局部作用域有效。你不能在头文件中声明auto workerFactory();,因为这会引起编译错误。

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

相关问题