如何在C 17中定义功能组成

How to define the function composition in c++17?

本文关键字:定义 功能      更新时间:2023-10-16

我想计算函数组成-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;
}

我收到的消息是它无法识别第一个功能的类型。

btw,这真的是您的代码吗?您不扩展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)...));
    };
}

(请注意by-values绑定。在C 17中,它们比二十年前更先进。:)您可以通过口味添加std::move s和 std::forward s。(

这样,您可以构成两个功能:

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的描述

如果存储的参数arg为t型的 std::is_bind_expression<T>::value == true(例如,另一个绑定表达式直接传递到初始调用以绑定到绑定(,则绑定函数函数组成:而不是传递函数对象,即绑定子表达会将绑定对象返回,急切地调用子表达,并将其返回值传递给外部调用对象。如果绑定子表达具有任何占位符参数,则将与外部绑定共享(从u1, u2, ...中挑选(。具体来说,上面的std::invoke调用中的参数vnarg(std::forward<Uj>(uj)...),同一调用中的Vn类型为std::result_of_t<T cv &(Uj&&...)>&&(CV资格与G的CV级相同(。

对不起,此刻没有例子。> _&lt;

P.S。是的,std::round是一个超载函数,因此您应该将其输入以指定您需要组合的确切过载。

random的包含 cmath,在默认名称空间以及std名称空间中的libstdc++中还定义了几个数学运算符(包括round(。(有关基本原理,请参见此答案。(和C 的round具有多个过载。结果,您有几个版本的round可用,并且您的功能不知道要使用哪个round,因此有关歧义的错误消息。正确的解决方案是消除您的意思是哪个round。您可以使用静态演员进行此操作:

static_cast<double(*)(double)>(round)

由于您必须经过麻烦,因此您也可以使用cmath标头代替math.h并改用std::round。至少这样您就知道它会超负荷。