Polymorphic(Generic)函数作为C++中的参数

use*_*934 6 c++ generics higher-kinded-types variadic-templates higher-rank-types

我正在开发一个相对简单的程序(实际上是一个计算器).但是,我决定尽可能使我的程序的所有组件都是通用的,因为:

  1. 这是一个很好的做法.
  2. 它让事情变得有趣.

作为该计划的一部分,我正在使用Tuple我正在编写的课程.我知道一个类已经存在,但我喜欢完全控制我的代码,这只是一个练习.

我需要做的一件事是将表达式元组(表达式本身是通用的)转换为包含表达式求值结果的元组.简而言之,我(遗漏了一些琐碎的部分):

template <class T>
class Expression {

    public:
        virtual T Eval() = 0;

    // ...
};

template <class First, class ... Rest>
class Tuple {

    // ...

    private:
        First first;
        Tuple<Rest ...> rest;
};
Run Code Online (Sandbox Code Playgroud)

我想专门研究像这样的泛型类型的元组:

template <template <class> class R, class First, class ... Rest>
class Tuple<R<First>, R<Rest> ...> {

    // and here is the problem:
    Tuple<First, Rest ...> Transform(function<template<class T> T(R<T>)>);
};
Run Code Online (Sandbox Code Playgroud)

之后我可以这样做:

template <class T> // There has to be a better way to do this
T Eval(Expression<T>& expr){
    return expr.Eval();
}

// ...
Tuple<First, Rest ...> tuple = exprs.Transform(Eval);
Run Code Online (Sandbox Code Playgroud)

在这里有几个地方,我不知道如何去做事情,一个可以帮助我在这里的真正的专家将不胜感激.我希望这段代码不会因为小错误而编译,但这不是重点 - 我主要担心的是我标记的行.如果我从短暂的时期内正确地回忆起我学会了Haskell这个函数应该是Rank-2(如果没有请注释,我将删除标签).它看起来不对劲.有没有办法做到这一点?

更新:

我被建议尝试传递一个带有泛型operator ()作为模板参数的仿函数,但这也不起作用.

Bar*_*rry 2

我认为你可以非常简单地做到这一点,根本不需要 C++14。我将假设有关 your 的Tuple构建方式的一些事情,即这两个因素存在:

Tuple(First, Rest... );                // (1)
Tuple(First, const Tuple<Rest...>& );  // (2)
Run Code Online (Sandbox Code Playgroud)

我们需要一种类型特征:给定一个我们要转换的函数,我们需要知道它产生什么类型:

template <typename T, typename F>
using apply_t = decltype(std::declval<F>()(std::declval<T>()));
Run Code Online (Sandbox Code Playgroud)

(天哪,我爱 C++11)

这样,我们就可以轻松确定返回类型,只需递归调用该函数即可:

template <typename First, typename... Rest>
struct Tuple
{
    template <typename F>
    Tuple<apply_t<First, F>, apply_t<Rest, F>...>
    Transform(F func)
    {
        return {func(first), rest.Transform(func)}; // hence the need
                                                    // for ctor (2)
    };
};
Run Code Online (Sandbox Code Playgroud)

(根据您的编写方式,Tuple您可能需要也可能不需要仅返回 a 的简单转换的基本情况Tuple<>,或者仅返回 a 的基本情况Tuple<apply_t<First, F>>。无论哪种方式,都没什么大不了的)。

而且您甚至根本不需要专门化Tuple。您只需要传递正确的函子即可。例如:

struct Zero
{
    template <typename T>
    int operator()(T ) { return 0; }
};

struct Incr
{
    template <typename T>
    T operator()(T x) { return x + 1; }
};

Tuple<int, double, char> tup(1, 2.0, 'c');
auto z = tup.Transform(Zero{}); // z is Tuple<int, int, int>{0, 0, 0}
auto i = tup.Transform(Incr{}); // i is Tuple<int, double, char>{2, 3.0, 'd'}
Run Code Online (Sandbox Code Playgroud)

是一个完整的代码示例,也记录了所有类型。当然,使用 C++14,我们可以内联执行这些操作:

auto i2 = tup.Transfom([](auto x) -> decltype(x) {return x+1; });
// i2 is a Tuple<int, double, char>{2, 3.0, 'd'};
// without the trailing decltype, it gets deduced as Tuple<int, double, int>.
Run Code Online (Sandbox Code Playgroud)