如何使用捕获实现 lambda 的包装器?

问题描述

std::function 可以用捕获列表包装 lambda,但是 std::function 是如何实现的?我们不知道它的类型是什么,它可以分配给 void* 但这样就不可能调用 lambda

解决方法

免责声明:这是不完整的,可能不正确,并且不遵循 std::function api 和约定(例如,它不可复制)。这只是一个示例,展示了如何实现 std::function 之类的类型的类型擦除的一种方法,精简到最低限度以了解类型擦除技术。

为了存储任何类型并能够根据函数签名调用它,我们使用了多态性。

这是定义调用 API 的 Signature 的基本抽象类。 Signature 必须是函数类型:

template <class Signature> struct CallableBase;

template <class R,class... Args>
struct CallableBase<R(Args...)>
{
    virtual R call(Args... args) = 0;
};

接下来我们需要一个派生类来保存实际的可调用对象 F 并且可以调用它:

template <class F,class Signature> struct Callable;

template <class F,class R,class... Args>
struct Callable<F,R(Args...)> : CallableBase<R(Args...)>
{
    F f;

    Callable(F f) : f{f} {}

    R call(Args... args) override
    {
        return f(args...);
    }
};

在我们的 Function 类中,我们持有一个类型为 CallableBase 的指针,指向类型为 Callable<F> 的对象以启用多态并获得所需的行为:

template <class Signature> struct Func;

template <class R,class... Args>
struct Func<R(Args...)>
{
    using Signature = R(Args...);

    std::unique_ptr<CallableBase<Signature>> callable_;

    template <class F>
    Func(F f) : callable_{std::make_unique<Callable<F,Signature>>(f)} {}

    R operator()(Args... args)
    {
        return callable_->call(args...);
    }
};

这让我们删除了一个基本类型Function

auto test()
{
    int x = 10;

    Func<int(int,int)> f = [=] (int a,int b) { return a * x + b; };

    return f(2,5); // 25
}