如何使用仿函数的工厂模式?

时间:2013-04-08 13:19:34

标签: c++ templates factory functor

我有一组函子用于计算对象范围内的特定内容。基本上,每个仿函数都实现operator()

template <typename Iterator1,
          typename Iterator2> double operator()( Iterator1 it1,
                                                 Iterator2 it2 ) const
{
  return( 0.0 );
}

我现在有一组可以创建的对象与不同的仿函数。我通过模板创建者函数来解决这个问题:

template <class Functor> Foo createFoo( ... // some parameters for foo objects
                                        Functor f = Functor() )
{
  // calculate stuff for "foo" using functor "f"
}

我现在想要委托仿函数选择给我的程序用户,所以我决定创建一个仿函数工厂。给定一个仿函数的描述性名称,我想创建适当的仿函数,以便它可以用于创建所有Foo个对象,如上所述。

这是我陷入困境的地方:我无法创建一个返回模板仿函数的工厂,因为如果不编码我想要创建的仿函数的确切类型,我就无法调用此函数。

我考虑过使operator()成为某个基类的虚函数,即FunctorBase,但我不希望与虚函数调用相关的性能开销。避免上述开销是我首先选择使用模板的原因。

我在这里陷入僵局,并且肯定会感谢一些评论。

修改

我打算做什么(代码无效):

DistanceFunctor f = createFunctor( "Bar" ); // Create a functor from a client-supplied string

Foo createFoo( ..., // parameters for foo
               f );

在评论中,也建议使用虚拟功能。但是,如上所述的当前仿函数设计不适用于虚函数,因为编译器无法使函数模板成为虚拟函数。调整仿函数类以将两个Iterator类型作为模板参数是可能的,但非常笨重。

修改

仿函数的工作方式类似于FLANN中使用的仿函数。 See the git repository for an example。我没有看到如何以不同的方式指定这些仿函数。

1 个答案:

答案 0 :(得分:1)

在考虑了一下你的要求之后,这是我想出的第一件事: 您的不同仿函数显然无法按照现在定义的方式从公共基类派生,因为您无法生成模板operator()虚拟。但是,如果您将模板参数从操作员推送到仿函数本身,可以使该操作符成为虚拟:

template <class Iterator>
struct Functor {
  virtual double operator()(Iterator it1, Iterator it2) const = 0;
};

这有什么帮助?初看Functor级别的模板看起来不太好,因为您现在必须知道createFunctor调用之外的参数,实际上,如果调用它,则必须显式指定迭代器类型:

//some concrete functors
template <class It>
struct FuncImpl_Bar : Functor<It> { /* ... implement op()(It, It) ... */ }:

template <class It>
struct FuncImpl_Meow : Functor<It> { /* ... implement op()(It, It) ... */ }:

template <class Iterator>
std::shared_ptr<Functor<Iterator>> createFunctor(std::string const& str)
{
  if (str == "Bar") return std::make_shared<FuncImpl_Bar<Iterator>>(/* args *);
  else return return std::make_shared<FuncImpl_Meow<Iterator>>(/* args *);
}

//...
auto MyFunctor = createFunctor<???>("Bar"); //what iterator type???

但是AFAICS你不需要知道createFoo之外的仿函数的确切类型 - 如果createFoo知道该类型就足够了:

Foo createFoo( ... // some parameters for foo objects
                                        std::string const& str ) //look mum, no templates!
{
  typedef whatever_the_container_i_use::iterator Iter;
  Functor<Iter> MyFunctor = createFunctor<Iter>(str);
  // calculate stuff for "foo" using functor "f"
}

//...
auto myFoo = createFoo(args, "Bar");
auto myOtherFoo = createFoo(args, "Moo");

简而言之:将工厂参数传递到您知道迭代器类型的位置,即您必须使用仿函数工厂参数化的类型。在工厂中调用所需的每个输入,即类型和非类型参数。