一、當模板類的某個引數為常量時,只特化類的某個成員函式
我們知道在c++模板程式設計中如果我們特化或是偏特化某個模板類, 我們需要重寫整個模板類中的所有函式, 但是這些**通常是非常相似的, 甚至在某些情況下可能只有一兩個函式會不一樣,其他函式都是一樣的。在這種情況下,同時存在多份相同的**,對我們維護這些**是非常不利的, 我們最好只需要特化其中不一樣的那個函式。
比如下面這個模板類:
只有當b等於16時, func這個函式需要特化, 但是其他函式無論什麼情況下都是一樣的。templatestruct base
};void test1()
int main()
下面是我們的一些可能解決方案:
方法1:
點評:通過偏特化實現,需要重寫所有的類成員方法。templatestruct base
};
方法2:
點評:通過執行時判斷,容易理解,但是相對低效。templatestruct base
else
}};
方法3:
點評:試圖通過預編譯來實現,但是這個方法是錯誤的。c++模板編譯包括預編譯,語法檢查,模板例項化等階段,在預編譯階段模板引數都還沒有例項化呢。templatestruct base
};
方法4:
點評:通過成員類以防函式的形式特化,增加了類成員變數。templatestruct base
};template<>
struct funcobj<16>
};funcobjfunc;
};
方法5:
點評:通過類成員模板函式特化來實現。templatestruct base
template<>
void funcimpl<16>()
void func()
};
方法6:
點評:根據int值的不同轉成不同的型別,然後通過函式過載實現。templatestruct base
; };
templatevoid funcimpl(const int2type)
void funcimpl(const int2type<16>)
void func()
};
方法7:
點評:和方法6類似,通過函式過載實現。namespace
; template struct conditional;
}templatestruct base
private:
struct primary_t ;
struct spec_t ;
void func_impl (primary_t)
void func_impl (spec_t )
};
方法8:
點評:通過enable_if,利用sfinae實現,跟方法7也有點類似。namespace
; template struct enable_if{};
}templatestruct base
template typename ::enable_if<16==n>::type
funcimpl()
void func()
};
我們可以看到根據編譯時模板引數int值的不同,我們重寫模板類的某個成員函式的方法是多種多樣的。針對上面這種情況,個人其實最推薦方法2,我們沒必要把簡單的問題複雜化。
二、當模板類的某個引數是某種型別時,只特化類的某個成員函式
下面我們考慮另外乙個需求, 當模板類的某個引數是某種型別時, 我們要求特化其中的乙個成員函式:
要求上面的模板類如果t2 是string型別,我們要求對func特殊重寫,其他的成員函式無論什麼情況實現都是一樣的。templatestruct base
};void test2()
int main()
有了上面的那個例子的實現經驗, 對這個問題我們解決就方便多了。
方法1:
方法2:templatestruct base
else
}};
點評:通過成員函式特化實現。templatestruct base
template<>
void funcimpl()
void func()
};
方法3:
點評:通過函式過載實現。templatestruct base
; templatevoid funimpl(const type2type)
templatevoid funimpl(const type2type)
void func()
};
方法4:
點評: 通過編譯時型別判斷實現。templatestruct isstring;};
template<>
struct isstring;
};templatestruct base
else
}};
方法5:
點評: 和方法4類似, 是不過實現方式不一樣。templatestruct must_be_same_type;};
template<>
struct must_be_same_type;
};template < typename t1,typename t2 >
class base
else
}};
最後,**下我自己遇到的問題, 我們在寫乙個事件委託(delegate)類,大概如下:
我們可以看到,當return_type是void時, 因為沒有返回值,上面的**會編譯失敗,因此我們只能偏特化這種情況:templateclass cevent
};void test3()
int main()
但是,我們會發現只有這個operator()函式是需要根據return_type特殊對待的,其他函式永遠都是一樣的。templateclass cevent
};
我們現在的問題就是如何只特化這個函式。
首先我們會想到如下的實現方法:
但是我們很快會發現這種情況下if語句被編譯進去了, 所以return_type是void的情況下還是會編譯失敗。templatestruct isvoid;};
template<>
struct isvoid;
};templateclass cevent
else
}};
我們要解決的問題就是如何把這個if語句變成函式過載,於是我們想到如下實現:
上面的實現首先通過編譯時型別識別,然後再把識別後相應的bool值轉成不同型別, 最後再利用不同型別函式過載實現。templatestruct isvoid;};
template<>
struct isvoid;
};templateclass int2type;};
templateclass cevent
return_type invokerimpl(first_type p1, second_type p2, int2type)
return_type operator() (first_type p1, second_type p2)
};
最後總結下,我們可以看到,從編譯時到執行時,從物件導向到普通范型程式設計再到模板元程式設計,c++複雜得讓人無語, 也強大得讓人無語, 而且c++語言本身是在不斷發展的(c++11), 同一問題在c++中往往有多種解決方案,這些解決方案有的簡單,有的複雜,有的高效, 也有的低效, 而我們的目標就是利用c++這把利器尋找簡單而高效的解決方案。
參考資料:
原出處:
(部分**或文字經本人整理或修飾)
C 模板程式設計中只特化模板類的乙個成員函式
模板程式設計中如果要特化或偏特化 區域性特化 乙個類模板,需要特化該類模板的所有成員函式。類模板中大多數成員函式的功能可能是一模一樣的,特化時我們可能只需要重新實現1 2個成員函式即可。在這種情況下,如果全部重寫該模板類的所有成員函式,不但會增加工作量,也不利於 的維護。例如下面的類模板a,只有在模...
C 如何只在堆 棧上建立類的物件
建構函式私有化 將類的建構函式私有,拷貝構造宣告成私有。防止別人呼叫拷貝在棧上生成物件。提供乙個靜態的成員函式,在該靜態成員函式中完成堆物件的建立。class heap only priivate c 98 類的構造和拷貝構造只宣告不定義a a const a c 11下的新寫法a delete a...
泛型程式設計 c 中的類模板的介紹
函式模板 通過使用者提供的具體的引數,c 編譯器能夠將函式模板例項化,根據同乙個模板建立出不同的具體的函式。這些函式之間的不同在於函式內部的一些資料型別不同函式模板的定義格式 template function definition 如 template template t max t a,t b...