内函子概念或接口

Endofunctor concept or interface

从这里开始,(endo)仿函数能够获取一个对象并将其转换为另一个相同类型的对象。函子最简单的例子就是恒等式:

struct Identity {
    template <typename T>
    T Apply(T x) {
        return x
    }
};

我需要一个 "Functor type" 来标识通用 Functor。我想做的是编写如下代码:

class Sum {
  public:
    Sum(Functor* f, Functor* g) :
        f_(f),
        g_(g) {}
    template <typename T>
    T Apply(T x) { return f_->Apply(x) + g_->Apply(x); }
  private
    Functor* f_;
    Functor* g_;
};

我想到的第一个想法当然是使用虚拟 class:

struct Functor {
    template <typename T>
    virtual T Apply(T x) = 0;
};

这种方法无法解决的问题是模板不能是虚拟的。

然后我尝试使用 C++ 概念。但是,如 Specifying a concept for a type that has a member function template using Concepts LiteC++ Concepts: Can I define a concept that is itself a template? 不可能 "templated concept"。

最后我偶然发现了 How to achieve "virtual template function" in C++,因此我想到了以下可能的实现方式:

struct Functor {
    template <typename T>
    T Apply(const T& x);  // No more virtual!!!
};

// ... Identity and Sum declarations properly inheriting from Functor ...

template <typename T>
T Functor::Apply(T x) {
    if (Identity* specialized =
            dynamic_cast<Identity*>(this)) {
        return specialized->Apply(x);
    } else if (const Sum* specialized =
            dynamic_cast<const Sum*>(this)) {
        return specialized->Apply(x);
    } else ...
}

虽然这是编译,但这不是最好的解决方案。主要问题是:性能和代码重复。 性能问题来自这样一个事实,即每次在 Functor 上调用 Apply 时,必须解析 Functor::Apply 中的长 if 子句。这是一个大问题,因为 Functor 可以深度嵌套(因此调用 Apply 可能会导致多次调用 Functor::Apply)。 "code repetition" 问题很明显,因为每次我想定义一个新的 Functor 时,我还必须修改 Functor::Apply 添加一个新的 if 子句。

我在这里要问的是,是否有合适的(更简洁的)方法来定义 Functor interface/concept,从而可以创建像 Sum 这样的 classes。 接受 C++ 概念和繁重的模板元编程。

p.s。所有代码片段都特意保持尽可能简单。避免建议使用 class 而不是 struct 或添加 const 标识符或使用唯一指针,这不是这个问题的重点。

不幸的是,我能想到的大多数(最佳)解决方案都要求您采用一些相当复杂的方法。当然,这不一定是坏事,但是当您继续设计程序时,它会使事情变得混乱。出于这个原因,我可能会提出一些更直接的建议:

template <typename F, typename G>
class Sum {
  public:          
    Sum(F& f, G& g) :
        f_(f),
        g_(g) {}
    template <typename T>
    inline T Apply(T x) { return f_.Apply(x) + g_.Apply(x); }
  private:
    F& f_;
    G& g_;
};

/*
    For every class like the above, you may want to define an 
    easy-to-use generating function to simplify instantiations: 
*/
template <typename F, typename G>
inline Sum<F, G> MakeSum(F& f, G& g)
{
    return Sum<F, G>(f, g);
}

#include <cmath>

struct SquareRoot {
    template <typename T>
    inline T Apply(T x)
    {
        return std::sqrt(x);
    }
};

struct Triple {
    template <typename T>
    inline T Apply(T x)
    {
        return T(3) * x;
    }
};

//    Example:

#include <iostream>

int main(void)
{
    using namespace std;
    SquareRoot square_root;
    Triple triple; 
    // For g++, don't forget to compile with -std=c++1z     
    auto sum = MakeSum(square_root, triple);
    cout << sum.Apply(1024) << endl;
}

诚然,它不如其他技术强大,但它可能是一个很好的起点。