如何在 std::function 中捕获 unique_ptr

问题描述 投票:0回答:2

我需要将

unique_ptr
移至
std::function
闭包。我在 C++14 中使用广义 lambda 捕获

auto ptr = make_unique<Foo>();

// Works.
auto lambda = [p = move(ptr)] { };

// This does not compile.
std::function<void()> func = [p = move(ptr)] { };

它试图将 lambda 捕获复制而不是移动到

std::function
中。相关错误是:

 copy constructor of '' is implicitly deleted because field '' has a deleted copy
      constructor
  std::function<void()> func = [p = move(ptr)] { };

这里的例子会让这看起来可行。

请注意,此处的答案只是重复 isocpp.org 上的示例。

我可以移动到

shared_ptr
,如下所示:

shared_ptr<Foo> s = move(ptr);

但这会产生另一个问题,因为我需要调用一个需要 lambda 中的

unique_ptr
的函数,并且我无法将
shared_ptr
转换回
unique_ptr

是否可以在

unique_ptr
中捕获
std::function

c++ c++14
2个回答
6
投票

std::function 对象都可以复制

std::function
是一个类型擦除对象,支持复制存储的对象。

当您将

std::unique_ptr
存储在 lambda 中时,该 lambda 不支持复制。

所以

std::function
的抱怨是正确的。它是一种可以复制的类型,当传入某些内容时,就会计算出如何复制它。 “我无法复制它”不是一个有效的答案;所有
std::function
都可以复制。

工业级实力解决方案:

有两种常见的方法可以解决这个问题。首先,将

std::function
的状态存储在某种
std::shared_ptr
中。其次,您编写或找到一个不可复制的
std::function
并使用它。

更“现代”

std::function
替换库支持许多有用的东西:

  1. 函数视图,不拥有它们所包装的内容。
  2. 仅移动函数对象,不支持复制。
  3. 多重重载函数对象,同时支持超过 1 个签名。
  4. 固定大小的缓冲区,如果没有足够的自动存储而不是堆分配,则无法编译。
  5. 可简单复制的函数对象

我个人出于各种特殊目的需要上述每一项。

然后,当您不需要复制可调用对象并且代码可以编译时,您可以使用

moveonly_function<void()>

但是,这对于您现在的需求来说可能太重了。

快速解决方案是:

template<class F>
auto make_shared_function( F&& f ) {
  return
   [pf = std::make_shared<std::decay_t<F>>(std::forward<F>(f))]
   (auto&&...args)->decltype(auto)
   {
     return (*pf)( decltype(args)(args)... );
   };
}

现在每当你遇到这个问题时:

// This does not compile.
std::function<void()> func = make_shared_function([p = move(ptr)] { });

可调用对象的状态现在存储在共享 ptr 中。


0
投票

从 C++23 开始,您现在可以使用 std::move_only_function 来实现您正在寻找的目标。

std::move_only_funciton
允许您将在 capute 块内移动变量的 lambda 分配给它。

$ g++ -std=c++23 -o output_file main.cpp
#include <iostream>
#include <functional>
#include <memory>

struct Foo
{
    int a;
    int b;
    int c;

    void Print()
    {
        std::cout << "Foo::Print is called" << std::endl;
    }
};

struct Caller
{
    std::move_only_function<void()> func;
};

int main()
{
    auto ptr = std::make_unique<Foo>();
    int a = 5;

    // This will compile since C++23.
    std::move_only_function<void()> func = [p = std::move(ptr), a]
    {
        p->Print();
        std::cout << "A is " << a << std::endl;
    };

    Caller o;
    o.func = std::move(func);
    o.func();
    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.