如何管理需要从递归函子/lambda 派生的模板参数的声明?

Ste*_*uer 2 c++ lambda y-combinator c++17

我正在尝试构建一个具有递归能力的 lambda 自作用域的干净整洁的实现(这基本上是一个 Y 组合器,尽管我认为技术上不完全)。这是一个旅程,它带我到许多其他人中,这个线程这个线程这个线程

我已经尽可能干净地归结了我的问题之一:如何传递以 lambda 作为模板参数的模板化函子?

#include <string>
#include <iostream>
#define uint unsigned int

template <class F>
class Functor {
public:
    F m_f;

    template <class... Args>
    decltype(auto) operator()(Args&&... args) {
        return m_f(*this, std::forward<Args>(args)...);
    }
};
template <class F> Functor(F)->Functor<F>;

class B {
private:
    uint m_val;
public:
    B(uint val) : m_val(val) {}
    uint evaluate(Functor<decltype([](auto & self, uint val)->uint {})> func) const {
        return func(m_val);
    }
};

int main() {
    B b = B(5u);
    Functor f = Functor{[](auto& self, uint val) -> uint {
        return ((2u * val) + 1u);
    }};

    std::cout << "f applied to b is " << b.evaluate(f) << "." << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

上面的代码不起作用,Visual Studio 声称f(在b.evaluate(f)调用中)与参数类型不匹配。

我的假设是auto & self不够聪明,无法完成这项工作。我该如何解决这个问题?当这些东西本质上无法定义时,我如何存储和传递它们?这就是为什么我见过的许多 Y-combinator 实现都有奇怪的双重包裹的东西吗?

任何帮助或解释将不胜感激。

max*_*x66 5

我看到的唯一方法是创建evaluate()一个模板方法;如果你想确保收到一个Functor(但你可以简单地接受一个可调用的:见Yakk 的回答):

template <typename F>
uint evaluate(Functor<F> func) const {
    return func(m_val);
}
Run Code Online (Sandbox Code Playgroud)

考虑到每个 lambda 都是不同的类型,因为您可以使用以下简单代码进行验证

auto l1 = []{};
auto l2 = []{};

static_assert( not std::is_same_v<decltype(l1), decltype(l2)> );
Run Code Online (Sandbox Code Playgroud)

因此强加特定的 lambda 类型evaluate()无法工作,因为如果您使用(显然)相同的 lambda 函数调用该方法,则调用不匹配,如下面的示例所示

auto l1 = []{};
auto l2 = []{};

void foo (decltype(l1))
 { }

int main ()
 {
   foo(l2); // compilation error: no matching function for call to 'foo'
 }
Run Code Online (Sandbox Code Playgroud)