包装一个接受 std::function<double(doule)> 的函数,以便传递接受更多参数的函数
Wrap a function that takes std::function<double(doule)> in order to pass functions that take more arguments
问题
我有一个函数 double subs(std::function<double(double)> func)
,我想将它包装在一个新函数中,如下所示
template<typename... Args> double subsWrap(std::function<double(Args... args)> func)
将subs
应用于需要更多输入的函数,如
subs( subs( subs( ... func(...) ) ) )
每个 subs
一次仅应用于 func
的参数之一。
最小示例
假设我们有一个函数
auto subs = [] (std::function<double(double)> func){return func(2) + func(5.3);};
我们想将它应用到
auto f2=[](double x, double y){return std::sin(x*std::exp(x/y)); };
作为
subs( [&f2](double y){ return subs( [&y,&f2](double x){ return f2(x,y); } ); } )
对于f2
,这很容易,所以不需要包装器。然而,如果我们想对一个有更多参数的函数做同样的事情(例如 double(double,double,double,double,double)
),事情就会变得复杂起来。
必须有一种方法可以自动执行此操作,但我什至不确定如何开始。
如何使用可变参数 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...); }
);
});
}
这里需要明确的 return lambda 类型规范来传播“可调用性”属性。 [=](auto... xs) { return func(x, xs...); }
可以使用任意数量的参数正式调用,无论 func(x, xs...)
是否可调用。当使用 decltype
明确指定 return 类型时,SFINAE 会跳入。
通过此实现,两个表达式
subs([=](double y) {
return subs([=](double x) {
return f2(x, y);
});
});
和
subs_wrap(f2);
将产生相同的结果。
值得注意的是,通过 -O3
优化,GCC 和 Clang 都可以 optimize all this code away 并将 subs_wrap(f2)
替换为 compile-time 常量。对于使用 std::function
个参数编写的类似代码,他们不会这样做。
How do we do the unpacking if we want to pass arguments to subs (different for each recursion)
这是一种通过稍微修改代码来实现此目的的方法:
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);
问题
我有一个函数 double subs(std::function<double(double)> func)
,我想将它包装在一个新函数中,如下所示
template<typename... Args> double subsWrap(std::function<double(Args... args)> func)
将subs
应用于需要更多输入的函数,如
subs( subs( subs( ... func(...) ) ) )
每个 subs
一次仅应用于 func
的参数之一。
最小示例
假设我们有一个函数
auto subs = [] (std::function<double(double)> func){return func(2) + func(5.3);};
我们想将它应用到
auto f2=[](double x, double y){return std::sin(x*std::exp(x/y)); };
作为
subs( [&f2](double y){ return subs( [&y,&f2](double x){ return f2(x,y); } ); } )
对于f2
,这很容易,所以不需要包装器。然而,如果我们想对一个有更多参数的函数做同样的事情(例如 double(double,double,double,double,double)
),事情就会变得复杂起来。
必须有一种方法可以自动执行此操作,但我什至不确定如何开始。
如何使用可变参数 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...); }
);
});
}
这里需要明确的 return lambda 类型规范来传播“可调用性”属性。 [=](auto... xs) { return func(x, xs...); }
可以使用任意数量的参数正式调用,无论 func(x, xs...)
是否可调用。当使用 decltype
明确指定 return 类型时,SFINAE 会跳入。
通过此实现,两个表达式
subs([=](double y) {
return subs([=](double x) {
return f2(x, y);
});
});
和
subs_wrap(f2);
将产生相同的结果。
值得注意的是,通过 -O3
优化,GCC 和 Clang 都可以 optimize all this code away 并将 subs_wrap(f2)
替换为 compile-time 常量。对于使用 std::function
个参数编写的类似代码,他们不会这样做。
How do we do the unpacking if we want to pass arguments to subs (different for each recursion)
这是一种通过稍微修改代码来实现此目的的方法:
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);