C++中的通用Functor类

3
我正在尝试在这里构建一个线程:Variable length template arguments list?的基础上,建立一个默认的Functor类,这只是学术兴趣。我的目标是构建一个通用的Functor类:给定一个类名,方法名和参数类型(可变长度),它将构建一个具有operator()方法的类,该方法接受指定模板参数类型的可变数量的参数,并接受指针并应用给定的方法。想象一下这样一个类:
class MyClass
{
 public:
   float Fraction( float n, int m)
   {
       return n/m;
   }
   int Increment(int n)
   {
       return n+1;
   }
} ;

还有一个模版化的函数对象类,可以在任何函数中使用,例如:

int k = FunctorClass<MyClass, Increment, int, int /*return type*/> (3);
assert(k == 4);
float l = FunctorClass<MyClass, Fraction,  float, int, float, /*return type*/> (4,3);
assert(l == (4/3));

能否构建这样的函数类? 附注:不能使用可变参数模板(在VS2010中编写,没有...模板参数) 感谢您的帮助

2个回答

3
这是可以实现的,例如Boost bind()在底层使用了这种方法。但是,如果没有可变参数,您将无法获得完全的通用性,因为您将受到固定数量的模板参数的限制,并且需要为要支持的每个不同数量的参数键入实现。此外,如果没有右值引用,您将无法获得完美转发。
话虽如此,您尝试使用它的方式是行不通的:在声明成员函数时,不能仅仅命名它们。您需要使用例如&MyClass::Increment&MyClass::Fraction来获取正确的成员函数指针。如果成员函数是重载的,则需要消除歧义。
由于您显然希望使该函数对象能够用于非静态成员函数,因此您还需要提供一个要调用成员函数的对象。这方面最合理的方法是将对象的引用作为函数对象类的构造函数参数传递并存储以在调用函数时使用。也就是说,使用看起来有些不同,但可以通过某种工厂函数简化。以下是一种调整各种内容并实现相应函数对象模板的版本:
#include <cassert>

// -----------------------------------------------------------------------------

template <typename T, T> class FunctorClass;

template <typename RC, typename Class,
          RC (Class::*Member)()>
class FunctorClass<RC (Class::*)(), Member>
{
public:
    FunctorClass(Class& object): object_(&object) {}
    RC operator()() const { return (this->object_->*Member)(); }
private:
    Class* object_;
};

template <typename RC, typename Class, typename A0,
          RC (Class::*Member)(A0)>
class FunctorClass<RC (Class::*)(A0), Member>
{
public:
    FunctorClass(Class& object): object_(&object) {}
    RC operator()(A0 a0) const { return (this->object_->*Member)(a0); }
private:
    Class* object_;
};

template <typename RC, typename Class, typename A0, typename A1,
          RC (Class::*Member)(A0, A1)>
class FunctorClass<RC (Class::*)(A0, A1), Member>
{
public:
    FunctorClass(Class& object): object_(&object) {}
    RC operator()(A0 a0, A1 a1) const { return (this->object_->*Member)(a0, a1); }
private:
    Class* object_;
};

// -----------------------------------------------------------------------------

class MyClass
{
 public:
    int foo() { return 17; }
    float Fraction( float n, int m)
    {
        return n/m;
    }
    int Increment(int n)
    {
        return n+1;
    }
};

int main()
{
    MyClass object;
    int i = FunctorClass<int (MyClass::*)(), &MyClass::foo>(object)();
    assert(i == 17);

    int k = FunctorClass<int (MyClass::*)(int), &MyClass::Increment>(object)(3);
    assert(k == 4);
    float l = FunctorClass<float (MyClass::*)(float, int), &MyClass::Fraction>(object)(4,3);
    assert(l == (4.0f/3));
}

感谢您认真思考并给出的答案。是的,我的情况是针对非静态成员,并且必须发送对象。 您能详细解释一下吗:此外,如果没有右值引用,您将无法获得完美转发。 再次感谢。 - user1186270
C++2011支持右值引用(对于某些类型T,拼写为T&&),并且如果函数模板的类型以右值引用作为参数,则具有特殊的模板推导规则。这允许捕获参数是如何传递的(即它是来自左值还是来自临时对象),并将其转发到包装函数中。由于可以通过传递临时对象而不进行复制来传递,因此这可以防止某些在C++2003中不可能的复制。然而,完整的讨论需要一篇相当冗长的文章。 - Dietmar Kühl
@user1186270:Dietmar是正确的。这确实会导致一篇相当冗长的文章。幸运的是,有人已经在这里写过了:http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx - ForeverLearning

1

我不确定您是否需要可变参数来完成此操作。考虑以下接口...

template < typename RETURN_TYPE >
class iFunctor abstract {

    public:

        virtual RETURN_TYPE operator () ( void ) = 0;

};

抽象接口不是完整的类,它们可以包含部分实现,例如函数签名和一些数据成员。使用模板,您可以泛化返回类型。但是,你说参数列表呢?

请注意接口中没有构造函数。在您的具体类(或派生类)中,您可以将可变参数列表的负担传递给构造函数,如下所示...

template < typename TYPE >
class ConcreteFunctor_add : public iFunctor < TYPE > {

    private:

       int A;
       int B;

    public:

       explicit ConcreteFunctor_add ( const int &a, const int &b ) : A(a), B(b) {};

       TYPE operator () ( void ) { return ( A + B ); };

};

你通过构造函数逐个处理参数列表。

显式构造函数在声明时需要一个参数列表,因此你会在这里得到变量列表。所以在实践中...

ConcreteFunctor_add < int > addInteger ( 10, 10 );
addInteger();

......然后你就很酷了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接