C++ 11中具有可变参数模板的功能组合

Loï*_*ier 5 c++ functional-programming variadic-templates c++11

我是一名数学家,以前很长一段时间都在做"旧式"C++编程.我觉得C++ 11提供的一些新的句法结构可以帮助我实现一些关于我的专业项目的更好的代码.然而,由于我不是CS专业,我必须承认我缺乏了解我在自学过程中遇到的一些例子的知识,尽管到目前为止我还很幸运/成功.

我的印象是,可变参数模板可用于实现类型安全的函数组合,如本问题所示.由于我想用异构(但兼容)的参数/返回类型编写函数,所以我的关注稍微有点笼统.我已经google了很多,并找到了另一个参考,但它似乎对我来说是"黑魔法";)我不会假装我可以在我的上下文中调整代码,虽然我觉得我应该在那里找到我需要的东西.

我认为下面的(最不完整的)代码对于我想要实现的内容是相对不言自明的.特别是我相信当一个人试图组合不兼容的函数(这里是箭头)时,正确的实现将抛出编译时错误,并且需要一段递归模板代码.

template <typename Source , typename Target> class Arrow
{
  Target eval (const Source &);
};

template <typename ...Arrows> class Compositor
{
  template <typename ...Arrows>
  Compositor (Arrows... arrows)
  {
     // do/call what needs be here
  };

  auto arrow(); // gives a function performing the functionnal composition of arrows

};

// define some classes A, B and C

int main(int argc, char **argv)
{
  Arrow < A , B >  arrow1;
  Arrow < B , C >  arrow2;

  Compositor< Arrow < A , B > , Arrow < B , C > > compositor(arrow1 , arrow2);

  Arrow < A , C >  expected_result = compositor.arrow();
}
Run Code Online (Sandbox Code Playgroud)

理想情况下,我想
    Compositor
直接子类
    Arrow < source_of_first_arrow , target_of_last_arrow>
,并将方法
   arrow()
替换为相应的
    eval()

但我觉得上面的代码更具说明性.

任何帮助将不胜感激,即使它包含一个粗略的指责,指向一个现有的(相对基本的)示例,肯定会逃脱我的搜索.谢谢!

Gui*_*nal 6

如果我得到了正确的答案,你就不需要花哨的模板法术来完成这个组合.这是几乎不言自明的代码:

#include <functional>
#include <string>
#include <iostream>

// it is just an std::function taking A and returning B
template <typename A, typename B>
using Arrow = std::function<B(const A&)>;

// composition operator: just create a new composed arrow
template <typename A, typename B, typename C>
Arrow<A, C> operator*(const Arrow<A, B>& arr1, const Arrow<B, C>& arr2)
{
    // arr1 and arr2 are copied into the lambda, so we won't lose track of them
    return [arr1, arr2](const A& a) { return arr2(arr1(a)); };
}

int main()
{
    Arrow<int, float> plusHalf([](int i){return i + 0.5;});
    Arrow<float, std::string> toString([](float f){return std::to_string(f);});

    auto composed = plusHalf * toString; // composed is Arrow<int, std::string>
    std::cout << composed(6) << std::endl; // 6.5

    //auto badComposed = toString * plusHalf; // compile time error
}
Run Code Online (Sandbox Code Playgroud)

我主要在这里玩lambda函数.

使用单个函数调用而不是操作符链被证明是一个更棘手的问题.这次你有一些模板:

#include <functional>
#include <string>
#include <iostream>

// it is just an std::function taking A and returning B
template <typename A, typename B>
using Arrow = std::function<B(const A&)>;

// A helper struct as template function can't get partial specialization
template <typename... Funcs>
struct ComposerHelper;

// Base case: a single arrow
template <typename A, typename B>
struct ComposerHelper<Arrow<A, B>>
{
    static Arrow<A, B> compose(const Arrow<A, B>& arr)
    {
        return arr;
    }
};

// Tail case: more arrows
template <typename A, typename B, typename... Tail>
struct ComposerHelper<Arrow<A, B>, Tail...>
{
    // hard to know the exact return type here. Let the compiler figure out
    static auto compose(const Arrow<A, B>& arr, const Tail&... tail)
    -> decltype(arr * ComposerHelper<Tail...>::compose(tail...))
    {
        return arr * ComposerHelper<Tail...>::compose(tail...);
    }
};

// A simple function to call our helper struct
// again, hard to write the return type
template <typename... Funcs>
auto compose(const Funcs&... funcs)
-> decltype(ComposerHelper<Funcs...>::compose(funcs...))
{
    return ComposerHelper<Funcs...>::compose(funcs...);
}

using namespace std;

int main()
{
    Arrow<int, float> plusHalf([](int i){return i + 0.5;});
    Arrow<float, string> toString([](float f){return to_string(f);});
    Arrow<string, int> firstDigit([](const string& s){return s[0]-'0';});

    auto composed = compose(plusHalf, toString, firstDigit);
    // composed is Arrow<int, int>

    std::cout << composed(61) << std::endl; // "6"

    //auto badComposed = compose(toString, plusHalf); // compile time error
}
Run Code Online (Sandbox Code Playgroud)