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

标签 c++ c++17 auto

我想计算函数组合 -- 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 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).

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

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

关于c++ - 如何在c++17中定义函数组合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50011360/

相关文章:

c++ - 如何诊断/分析瞬时性能命中,C++

c++ - 增加优化标志时二进制文件怎么可能不改变?

c++ - 具有由整数确定的固定数量参数的函数

c++ - 使用 "auto"的声明是否与使用具体类型说明符的外部声明匹配?

c++ - 映射基于自动的循环单元素访问 C++

c++ - find_if 函数构建问题

c++ - glDeleteTextures 和 glDeleteBuffers 如何工作?

c++ - 如何在 C++ 中初始化返回元组的变量?

c++ - 将一组类转换为类模板并避免构造函数歧义

c++ - 为什么将 auto 推导出为 int 而不是 uint16_t