我想计算函数组合 -- f ( g (param) )。这是我尝试过的:
auto fComposition(auto&& f, auto&& g, auto&&... params)
{
/* some stuff */
auto result = std::forward<decltype(f)>(f)(
std::forward<decltype(g)>(g)(
std::forward<decltype(params)>(param)
)
);
/* other stuff */
return result;
};
编译
g++ -std=c++17 src.cpp
基本测试
#include <random>
#include <math.h>
int main()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<double> distr(-1.0, 1.0);
auto xx = fComposition(round, distr, gen);
return 0;
}
我收到消息说它无法识别第一个函数的类型。
最佳答案
顺便说一句,这真的是你的代码吗?你没有展开 params
所以它不应该编译。
我。您定义组合的方式与简单的调用没有区别:您的 fComposition(f, g, arg)
与f(g(arg))
相同除了额外的字符输入。真正的组合通常是一个组合子,它接受两个函数并返回一个闭包,当调用实际参数时,它会连续应用它们。像这样的东西:
template<class F, class G> auto comp(F f, G g) {
return [f, g](auto &&... args) {
return f(g(std::forward<decltype(args)>(args)...));
};
}
(注意按值绑定(bind)。在 C++17 中,它们比二十年前更先进。:) 您可以添加 std::move
s 和 std::forward
根据口味。)
这样你就可以组合两个函数:
auto fg = comp(f, g);
然后根据参数调用结果:
auto x = fg(arg1, arg2);
二。但实际上,为什么要用两个操作数来限制我们自己呢?在 Haskell 中,(.)
是一个单一的二元函数。在 C++ 中,我们可以拥有一整棵重载树:
template<class Root, class... Branches> auto comp(Root &&root, Branches &&... branches) {
return [root, branches...](auto &&...args) {
return root(branches(std::forward<decltype(args)>(args)...)...);
};
}
现在您可以将任何 AST 封装在一个可调用对象中:
int f(int x, int y) { return x + y; }
int g(int x) { return x * 19; }
int h(int x) { return x + 2; }
#include <iostream>
int main() {
auto fgh = comp(f, g, h);
std::cout << fgh(2) << '\n';
}
我所知道的在 11 标准之前的 C++ 中使用匿名闭包的唯一方法是类似的技术。
三。但是等等,有库解决方案吗?事实上,是的。来自 std::bind
's description
If the stored argument arg is of type T for which
std::is_bind_expression<T>::value == true
(for example, another bind expression was passed directly into the initial call to bind), then bind performs function composition: instead of passing the function object that the bind subexpression would return, the subexpression is invoked eagerly, and its return value is passed to the outer invokable object. If the bind subexpression has any placeholder arguments, they are shared with the outer bind (picked out ofu1, u2, ...
). Specifically, the argumentvn
in thestd::invoke
call above isarg(std::forward<Uj>(uj)...)
and the typeVn
in the same call isstd::result_of_t<T cv &(Uj&&...)>&&
(cv qualification is the same as that of g).
抱歉,目前这里没有示例。 >_<
附言是的,std::round
是一个重载函数,因此您应该对其进行类型转换以指定您需要组合的确切重载。
关于c++ - 如何在c++17中定义函数组合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50011360/