我想计算函数组成 - 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;
}
我收到的消息是它无法识别第一个函数的类型。
答案 0 :(得分:2)
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)...));
};
}
(注意 - 值绑定。在C ++ 17中,它们比二十年前更先进。:)你可以按品尝添加std::move
和std::forward
。)
这样你就可以组成两个函数:
auto fg = comp(f, g);
然后在参数上调用结果:
auto x = fg(arg1, arg2);
II。但实际上,为什么要用两个操作数限制自己呢?在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 ++进行匿名闭包的唯一方法。
III。但等等,是否有图书馆解决方案?事实上,是的。来自std::bind
's description
如果存储的参数arg是类型T,
std::is_bind_expression<T>::value == true
(例如,另一个绑定表达式直接传递给bind的初始调用),则bind执行函数组合:而不是传递函数对象bind子表达式将返回,子表达式被急切调用,其返回值被传递给外部的invokable对象。如果bind子表达式具有任何占位符参数,则它们将与外部绑定共享(从u1, u2, ...
中选择)。具体而言,上面vn
调用中的参数std::invoke
为arg(std::forward<Uj>(uj)...)
,同一调用中的Vn
类型为std::result_of_t<T cv &(Uj&&...)>&&
(cv资格与此相同g)。
对不起,此刻此处没有示例。 &GT; _&LT;
P.S。是的,std::round
是一个重载函数,所以你应该对它进行类型转换,以指定你需要组成哪个确切的重载。
答案 1 :(得分:0)
random
的包含cmath
,libstdc++
中的round
也定义了默认命名空间中的几个数学运算符(包括std
)以及round
{1}}命名空间。 (参见this answer的基本原理。)C ++的round
有多个重载。因此,您有几个版本的round
可用,并且您的函数不知道您要使用哪个round
,因此会出现有关歧义的错误消息。正确的解决方案是消除你的意思static_cast<double(*)(double)>(round)
。您可以使用静态强制转换来执行此操作:
cmath
由于您无论如何都要经历麻烦,您也可以使用math.h
标题代替std::round
并使用display: block !important;
代替。至少你知道它会在前面超载。