从这里开始,(endo)仿函数能够获取一个对象并将其转换为另一个相同类型的对象。最简单的仿函数示例是身份:
struct Identity {
template <typename T>
T Apply(T x) {
return x
}
};
我需要一个标识通用Functor的“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_;
};
我想到的第一个想法当然是使用虚拟类:
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 Lite和C++ Concepts: Can I define a concept that is itself a template?中所述 How to achieve "virtual template function" in C++不可能有一个“模板概念”。
最后我偶然发现了{{3}},因此我想出了以下可能的实现:
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中的long if子句。这是一个很大的问题,因为Functor可以深度嵌套(因此调用Apply可能会导致多次调用Functor :: Apply)。 “代码重复”问题非常明显,因为每次我想定义一个新的Functor时我都要修改Functor :: Apply添加一个新的if子句。
我在这里要问的是,是否有一种正确的(更干净的)方法来定义一个Functor接口/概念,可以创建像Sum这样的类。 接受C ++概念和重模板元编程。
P.S。所有代码片段都是有目的的尽可能简单。避免建议使用类而不是struct或添加const标识符或使用唯一指针,这不是这个问题的重点。
答案 0 :(得分:2)
不幸的是,我能想到的大多数(最佳)解决方案要求您采用一些相当复杂的方法。当然,这不一定是坏事,但随着您设计程序的推进,它可能会使事情变得混乱。出于这个原因,我可能会提出一些更直接的建议:
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;
}
当然,它没有其他技术那么强大,但它可能是一个很好的起点。