如何在c++17中定义函数组合?

1 c++ auto 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;
};
Run Code Online (Sandbox Code Playgroud)

编译用

g++ -std=c++17 src.cpp
Run Code Online (Sandbox Code Playgroud)

基本测试

#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;
}
Run Code Online (Sandbox Code Playgroud)

我收到消息说它无法识别第一个函数的类型。

bip*_*pll 5

顺便说一句,这真的是你的代码吗?您没有扩展,params所以它不应该编译。

I. 定义组合的方式,它与简单的调用没有区别:除了额外的字符输入之外,您的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)...));
    };
}
Run Code Online (Sandbox Code Playgroud)

(注意按值绑定。在 C++17 中,它们比二十年前更先进。:) 您可以根据喜好添加std::moves 和std::forwards。)

这样你就可以编写两个函数:

auto fg = comp(f, g);
Run Code Online (Sandbox Code Playgroud)

然后调用参数的结果:

auto x = fg(arg1, arg2);
Run Code Online (Sandbox Code Playgroud)

二. 但实际上,为什么要限制我们使用两个操作数呢?在 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)...)...);
     };
}
Run Code Online (Sandbox Code Playgroud)

现在您可以将任何 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';
}
Run Code Online (Sandbox Code Playgroud)

据我所知,类似的技术是在 11 标准之前的 C++ 中实现匿名闭包的唯一方法。

三.但是等等,有图书馆解决方案吗?事实上,是的。来自std::bind的描述

如果存储的参数 arg 的类型为 T std::is_bind_expression<T>::value == true(例如,另一个绑定表达式直接传递到对绑定的初始调用中),则绑定执行函数组合:而不是传递绑定子表达式将返回的函数对象,而是传递子表达式被急切地调用,并且其返回值被传递给外部可调用对象。如果绑定子表达式有任何占位符参数,它们将与外部绑定共享(从 中挑选u1, u2, ...)。具体来说,上面调用vn中的参数是,同一调用中的类型是(cv 限定与 g 的限定相同)。std::invokearg(std::forward<Uj>(uj)...)Vnstd::result_of_t<T cv &(Uj&&...)>&&

抱歉,目前没有示例。>_<

PS 是的,std::round是一个重载函数,因此您应该对其进行类型转换以指定您需要组成哪个重载。