如何在c++17中定义函数组合?
How to define the function composition in c++17?
我想计算函数组合 -- 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;
}
我收到消息说它无法识别第一个函数的类型。
random
的包含包括 cmath
,它在 libstdc++
中还定义了默认命名空间中的几个数学运算符(包括 round
) std
命名空间。 (请参阅 this answer 了解基本原理。)并且 C++ 的 round
具有多个重载。结果,您有多个版本的 round
可用,而您的函数不知道您要使用哪个 round
,因此会出现关于歧义的错误消息。正确的解决方案是消除歧义 round
你的意思。您可以使用静态转换来做到这一点:
static_cast<double(*)(double)>(round)
反正麻烦也得过,你也可以用cmath
header代替math.h
,用std::round
代替。至少你知道它会预先超载。
顺便说一句,这真的是你的代码吗?您没有扩展 params
,所以它不应该编译。
我。您定义组合的方式与简单的调用没有区别:您的 fComposition(f, g, arg)
与 f(g(arg))
相同,只是输入了额外的字符。真正的组合通常是一个接受两个函数的组合子和一个 returns 闭包,当对实际参数调用时,它会连续应用它们。类似于:
template<class F, class G> auto comp(F f, G g) {
return [f, g](auto &&... args) {
return f(g(std::forward<decltype(args)>(args)...));
};
}
(注意按值绑定。在 C++17 中,它们比二十年前更先进。:) 您可以根据喜好添加 std::move
和 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 of u1, u2, ...
). Specifically, the argument vn
in the std::invoke
call above is arg(std::forward<Uj>(uj)...)
and the type Vn
in the same call is std::result_of_t<T cv &(Uj&&...)>&&
(cv qualification is the same as that of g).
抱歉,目前这里没有示例。 >_<
P.S。是的,std::round
是一个重载函数,因此您应该对其进行类型转换以指定您需要组合的确切重载。
我想计算函数组合 -- 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;
}
我收到消息说它无法识别第一个函数的类型。
random
的包含包括 cmath
,它在 libstdc++
中还定义了默认命名空间中的几个数学运算符(包括 round
) std
命名空间。 (请参阅 this answer 了解基本原理。)并且 C++ 的 round
具有多个重载。结果,您有多个版本的 round
可用,而您的函数不知道您要使用哪个 round
,因此会出现关于歧义的错误消息。正确的解决方案是消除歧义 round
你的意思。您可以使用静态转换来做到这一点:
static_cast<double(*)(double)>(round)
反正麻烦也得过,你也可以用cmath
header代替math.h
,用std::round
代替。至少你知道它会预先超载。
顺便说一句,这真的是你的代码吗?您没有扩展 params
,所以它不应该编译。
我。您定义组合的方式与简单的调用没有区别:您的 fComposition(f, g, arg)
与 f(g(arg))
相同,只是输入了额外的字符。真正的组合通常是一个接受两个函数的组合子和一个 returns 闭包,当对实际参数调用时,它会连续应用它们。类似于:
template<class F, class G> auto comp(F f, G g) {
return [f, g](auto &&... args) {
return f(g(std::forward<decltype(args)>(args)...));
};
}
(注意按值绑定。在 C++17 中,它们比二十年前更先进。:) 您可以根据喜好添加 std::move
和 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).
抱歉,目前这里没有示例。 >_<
P.S。是的,std::round
是一个重载函数,因此您应该对其进行类型转换以指定您需要组合的确切重载。