2012-02-19 58 views
2

我正试图在这里构建线程:Variable length template arguments list? 有一个默认的函子类,这只是学术上的兴趣。我的目标是构建一个通用的Fucntor类:给定类名称,方法名称和参数类型(可变长度),它构建一个具有operator()方法的类,该方法接受模板参数中指定类型的可变数量的参数,需要一个指针并应用给定的方法。想象一下,一类这样的:C++中的通用函数类

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

这当然是可行的,例如, 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)); 
} 
+0

谢谢你经过深思熟虑的答案。是的,我的情况是针对非静态成员,并且必须发送对象。 您能否详细介绍一下:另外,如果没有右值引用,您将无法获得完美的转发。 ? 再次感谢 – user1186270 2012-02-19 17:30:49

+0

如果函数模板将右值引用作为参数,那么C++ 2011支持rvalues引用(某些类型为'T'拼写'T &&')和特殊模板推导规则。这允许捕获参数如何传递(即它是来自左值还是来自临时值)并将其转发给包装函数。由于临时文件可以在不复制的情况下传递,因此可以防止某些C++ 2003无法实现的副本。但是,充分的讨论是一篇相当长的文章。 – 2012-02-19 22:46:50

+0

@ user1186270:Dietmar是对的。这确实会造成一篇相当长的文章。幸运的是,有人已经在这里写下了它:http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx – ForeverLearning 2012-02-23 22:10:52

0

我不知道你会需要variadics拉这一关。考虑以下接口...

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(); 

...你会很酷。