2012-02-19 61 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(); 

...你會很酷。