我正在编写几个构建随机森林的算法,每个森林将使用不同的数据和不同的函数进行训练(每个树将使用一组具有固定签名的函数,但不同的树将使用具有不同签名的不同函数集进行训练),然而,我希望只编写一次构建随机树的代码,使用模板。我目前的代码大致如下:
模板类 T 对应于训练数据类型(例如图像块或像素),模板类 V 对应于函数指针类型
template<class T, class V>class RandomTree{ void build(RandomTreeNode<T>& current_node, vector<V>& functions, vector<T>& data) { ... 一些基本调用函数并传入数据 T 的代码 }}
我创建对象的方式如下:
typedef double (*function_ptr)(TrainingDataPoint& data_point);RandomTree<TrainingDataPoint, function_ptr> tree = ...
问题是,出于效率考虑,我正在构建的一棵树需要函数集(function_ptr)不仅接受训练数据点(模板类型 T),还接受一个数据缓存。因此,我的函数指针将如下所示:
typedef double (*function_ptr)(TrainingDataPoint&, unordered_map<string, cv::Mat>& preloaded_images);
现在的问题是,我无法想到一种方法来保持 RandomTree 类的通用性,但同时让一些函数集(模板类型 V)接受的不仅仅是训练点(模板类型 T)。
到目前为止,我考虑过以下几种方法:
- 将缓存设为全局变量,以便函数可以访问它
- 为每个训练数据点添加一个指向缓存的指针(但谁负责清理?)
- 为 RandomTree 添加第三个模板参数,但在这种情况下,如果我构建的树不需要这个第三个参数,我应该放什么?
这些选项对我来说都不太吸引人,希望有人能提供一些经验,告诉我更好的方法?
谢谢
回答:
对于需要状态的函数,使用函子(functor)。在 C++ 中,函子是一个类(或结构体),它重载了 operator(),因此函子的实例可以像函数一样被“调用”。RandomTree 中函子的参数应该正是那些变化且由 RandomTree 控制的参数,其余的应该在外部绑定。一个带有额外状态的示例函子,用于包装函数:
template<typename Retval, typename Arg1, typename ExtraData>struct BindExtraData{ typedef Retval(*func_type)(Arg1, ExtraData); BindExtraData( ExtraData const& d_, func_type func_ ):d(d_), func(func_) {}; ExtraData d; func_type func; Retval operator()( Arg1 a1 ) { return func(a1, d); }};
但你可以做得更好。如果这是一次性的操作,就没有必要将其做成模板。bind2nd(嗯,binder2nd)是上述标准库版本,会编写得更好。