与可变参数 lambda 一起使用怎么样?std::is_invocable
输入特征来终止递归?
template<class Fn>
double subs_wrap(Fn func) {
if constexpr (std::is_invocable_v<Fn, double>)
return subs(func);
else
return subs([=](double x) {
return subs_wrap(
[=](auto... xs) -> decltype(func(x, xs...))
{ return func(x, xs...); }
);
});
}
这里需要 lambda 的显式返回类型规范来传播“可调用性”属性。[=](auto... xs) { return func(x, xs...); }
可以用任意数量的参数正式调用,无论是否func(x, xs...)
是否可调用。当使用显式指定返回类型时decltype
,SFINAE 介入。
通过这个实现,两个表达式
subs([=](double y) {
return subs([=](double x) {
return f2(x, y);
});
});
and
subs_wrap(f2);
会产生相同的结果。
有趣的是,与-O3
GCC 和 Clang 都可以优化优化所有这些代码 https://godbolt.org/z/z5TqrsnvM并替换subs_wrap(f2)
带有编译时常量。使用类似的代码编写std::function
他们不这样做的论点。
如果我们想将参数传递给 subs,我们如何进行解包(每个递归都不同)
这是通过稍微修改代码来实现此目的的方法:
template<class Fn, class P, class... Ps>
double subs_wrap(Fn func, P p, Ps... ps) {
if constexpr (std::is_invocable_v<Fn, double>) {
static_assert(sizeof...(Ps) == 0);
return subs(func, p);
}
else {
static_assert(sizeof...(Ps) > 0);
return subs([=](double x) {
return subs_wrap(
[=](auto... xs) -> decltype(func(x, xs...))
{ return func(x, xs...); },
ps...);
}, p);
}
}
subs_wrap(f2, p1, p2);