程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 在C++范型編程中如何只特化類的一個成員函數

在C++范型編程中如何只特化類的一個成員函數

編輯:C++入門知識

我們知道在C++模板編程中如果我們特化或是偏特化某個模板類, 我們需要重寫整個模板類中的所有函數, 但是這些代碼通常是非常相似的, 甚至在某些情況下可能只有一兩個函數會不一樣,其他函數都是一樣的。在這種情況下,同時存在多份相同的代碼,對我們維護這些代碼是非常不利的, 我們最好只需要特化其中不一樣的那個函數。   比如下面這個模板類:       template<typename T, unsigned B> struct Base {     //other function     //....     void Func(){ cout << "primary function" << endl; } }; void test1() {     Base<int, 1> a;     a.Func();     Base<int, 16> b;     b.Func(); } int main() {      test1(); }         只有當B等於16時, Func這個函數需要特化, 但是其他函數無論什麼情況下都是一樣的。   下面是我們的一些可能解決方案:   方法1:       template<typename T> struct Base<T, 16> {     //other function     //....     void Func(){ cout << "specialization function" << endl; } };     點評:通過偏特化實現,需要重寫所有的類成員方法。       方法2:       template<typename T, unsigned B> struct Base {     //other function     //....     void Func()     {         if(B == 16)         {             cout << "primary function" << endl;         }         else         {             cout << "specialization function" << endl;         }     } };     點評: 通過運行時判斷, 容易理解,但是相對低效。       方法3:       template<typename T, unsigned B> struct Base {     //other function     //....     void Func()     { #if B!=16             cout << "primary function" << endl; #else             cout << "specialization function" << endl; #endif     } };     點評: 試圖通過預編譯來實現,但是這個方法是錯誤的。C++模板編譯包括預編譯,語法檢查,模板實例化等階段,在預編譯階段模板參數都還沒有實例化呢。       方法4:       template<typename T, unsigned B> struct Base {     //other function     //....     template<unsigned S>     struct FuncObj     {         void operator()()         {             cout<<"primary function"<<endl;         }     };     template<>     struct FuncObj<16>     {         void operator()()         {             cout<<"specialization function"<<endl;         }     };     FuncObj<B> Func; };     點評: 通過成員類以防函數的形式特化, 增加了類成員變量。       方法5:       template<typename T, unsigned B> struct Base {     //other function     //....     template<unsigned N>     void FuncImpl()     {         cout<<"primary function"<<endl;     }     template<>     void FuncImpl<16>()     {         cout<<"specialization function"<<endl;     }     void Func()     {         FuncImpl<B>();     } };     點評:通過類成員模板函數特化來實現。       方法6:       template<typename T, unsigned B> struct Base {     //other function     //....     template<unsigned N>      class Int2Type     {         enum { value = N };     };     template<unsigned V>     void FuncImpl(const Int2Type<V>)     {         cout<<"primary function"<<endl;     }     void FuncImpl(const Int2Type<16>)     {         cout<<"specialization function"<<endl;     }     void Func()     {         FuncImpl(Int2Type<B>());     } };     點評: 通過將int根據值的不同轉成不同的類型,然後通過函數重載實現。       方法7:       namespace {     template <bool,typename T,typename> struct conditional { typedef T type; };     template <typename T,typename U> struct conditional<false,T,U> {typedef U type; }; } template<class T, unsigned B> struct Base {     //other function     //....       void Func ()     {         typedef typename ::conditional<B!=16,primary_t,spec_t>::type type;         Func_impl(type());     } private:     struct primary_t { };     struct spec_t    { };     void Func_impl (primary_t) { std::cout << "primary function" << std::endl; }     void Func_impl (spec_t   ) { std::cout << "specialization function" << std::endl; } };     點評: 和方法6類似,通過函數重載實現       方法8:    namespace    {     template <bool,typename T = void> struct enable_if { typedef T type; };     template <typename T> struct enable_if<true,T> {}; } template<class T, unsigned B> struct Base {     //other function     //....       template <unsigned N>     typename ::enable_if<16!=N>::type         FuncImpl () { std::cout << "primary function" << std::endl; }     template <unsigned N>     typename ::enable_if<16==N>::type         FuncImpl () { std::cout << "specialization function" << std::endl; }     void Func() {         FuncImpl<B>();     } }; 點評:通過enable_if, 利用SFINAE實現。       我們可以看到根據編譯時模板參數int值的不同,我們重寫模板類的某個成員函數的方法是多種多樣的。針對上面這種情況,個人其實最推薦方法2,我們沒必要把簡單的問題復雜化。       下面我們考慮另外一個需求, 當模板類的某個參數是某種類型時, 我們要求特化其中的一個成員函數:       template<typename T1, typename T2> struct Base {     //other function     //....     void Func(){ cout << "primary function" << endl; } }; void test2() {     Base<int, int> a;     a.Func();     Base<int, string> b;     b.Func(); } int main() {     test2(); }     要求上面的模板類如果T2 是string類型, 我們要求對Func特殊重寫,其他的成員函數無論什麼情況實現都是一樣的。   有了上面的那個例子的實現經驗, 對這個問題我們解決就方便多了。       方法1:       template<typename T1, typename T2> struct Base {     //other function     //....     void Func()     {         if(typeid(std::string) == typeid(T2))         {             cout<<"specialization function"<<endl;         }         else         {             cout << "primary function" << endl;          }     } };     點評:通過運行時類型識別(RTTI)實現,需要打開相關編譯選項,並且低效。       方法2:       template<typename T1, typename T2> struct Base {     //other function     //....     template<typename T>     void FuncImpl()     {         cout << "primary function" << endl;      }     template<>     void FuncImpl<string>()     {         cout << "specialization function" << endl;      }     void Func()     {         FuncImpl<T2>();     } };     點評:通過成員函數特化實現       方法3:       template<typename T1, typename T2> struct Base {     //other function     //....     template<typename T>      class Type2Type     {         typedef T type;     };     template<typename T>     void FunImpl(const Type2Type<T>)     {         cout << "primary function" << endl;      }     template<typename T>     void FunImpl(const Type2Type<string>)     {         cout << "specialization function" << endl;      }     void Func()     {         FunImpl<T2>(Type2Type<T2>());     } };     點評: 通過函數重載實現       方法4:       template<typename T> struct IsString {     enum { value = false }; }; template<> struct IsString<string> {     enum { value = true }; }; template<typename T1, typename T2> struct Base {     //other function     //....     void Func()     {          if(IsString<T2>::value)         {             cout << "specialization function" << endl;          }         else         {             cout << "primary function" << endl;          }     } };     點評: 通過編譯時類型判斷實現。       方法5:       template<typename T3,  typename T4> struct must_be_same_type {     enum { ret = 0 }; }; template<> struct must_be_same_type<string, string> {     enum { ret = 1 }; }; template < typename T1,typename T2 > class Base{ public:     //other function     //....     void Func(){         if(must_be_same_type<T2, string>::ret)         {             cout << "specialization function" << endl;          }         else         {             cout << "primary function" << endl;          }     } };     點評: 和方法4類似, 是不過實現方式不一樣。       最後,探討下我自己遇到的問題, 我們在寫一個事件委托(delegate)類,大概如下:       template<typename return_type, typename first_type, typename second_type> class CEvent  { public:     //other function     //....     return_type operator()(first_type p1, second_type p2)     {         return_type ret = return_type();         //...         //ret = invoker(p1, p2);         return ret;     } }; void test3() {     CEvent<int, int, int> e1;     e1(1, 2);     CEvent<void, int, int> e2;     e2(1, 2); } int main() {     test3(); }     我們可以看到,當return_type是void時, 因為沒有返回值,上面的代碼會編譯失敗,因此我們只能偏特化這種情況:       template<typename first_type, typename second_type> class CEvent<void, first_type, second_type> { public:     //other function     //....     void operator()(first_type p1, second_type p2)     {         //...         //invoker(p1, p2);         return;     } };     但是,我們會發現只有這個operator()函數是需要根據return_type特殊對待的,其他函數永遠都是一樣的。   我們現在的問題就是如何只特化這個函數。       首先我們會想到如下的實現方法:       template<typename T> struct IsVoid {     enum { value = false }; }; template<> struct IsVoid<void> {     enum { value = true }; }; template<typename return_type, typename first_type, typename second_type> class CEvent  { public:     other function     ....     return_type operator()(first_type p1, second_type p2)     {         if(IsVoid<return_type>::value)         {             cout << "return type is void" << endl;             //...             //invoker(p1, p2);         }         else         {             cout << "return type is not void" << endl;             return_type ret = return_type();             //...             //ret = invoker(p1, p2);             return ret;         }     } };     但是我們很快會發現這種情況下if語句被編譯進去了, 所以return_type是void的情況下還是會編譯失敗。   我們要解決的問題就是如何把這個if語句變成函數重載,於是我們想到如下實現:       template<typename T> struct IsVoid {     enum { value = false }; }; template<> struct IsVoid<void> {     enum { value = true }; }; template<int v> class Int2Type {     enum {value = v }; }; template<typename return_type, typename first_type, typename second_type> class CEvent  { public:     //other function     //....     return_type InvokerImpl(first_type p1, second_type p2, Int2Type<true>)     {         cout << "return type is void" << endl;         //...         //invoker(p1, p2);     }www.2cto.com     return_type InvokerImpl(first_type p1, second_type p2, Int2Type<false>)     {         cout << "return type is not void" << endl;         return_type ret = return_type();         //...         //ret = invoker(p1, p2);         return ret;     }     return_type operator()(first_type p1, second_type p2)     {         return InvokerImpl(p1, p2, Int2Type<IsVoid<return_type>::value>());     } };     上面的實現首先通過編譯時類型識別,然後再把識別後相應的bool值轉成不同類型, 最後再利用不同類型函數重載實現。       最後總結下,我們可以看到,從編譯時到運行時,從面向對象到普通范型編程再到模板元編程,C++復雜得讓人無語, 也強大得讓人無語, 而且C++語言本身是在不斷發展的(C++11), 同一問題在C++中往往有多種解決方案,這些解決方案有的簡單,有的復雜,有的高效, 也有的低效, 而我們的目標就是利用C++這把利器尋找簡單而高效的解決方案。    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved