模板:什么是Traits
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
Traits不是一種語(yǔ)法特性,而是一種模板編程技巧。Traits在C++標(biāo)準(zhǔn)庫(kù),尤其是STL中,有著不可替代的作用。
?
如何在編譯期間區(qū)分類(lèi)型
?
下面我們看一個(gè)實(shí)例,有四個(gè)類(lèi),Farm、Worker、Teacher和Doctor,我們需要區(qū)分他們是腦力勞動(dòng)者還是體力勞動(dòng)者。以便于做出不同的行動(dòng)。
這里的問(wèn)題在于,我們需要為兩種類(lèi)型提供一個(gè)統(tǒng)一的接口,但是對(duì)于不同的類(lèi)型,必須做出不同的實(shí)現(xiàn)。
我們不希望寫(xiě)兩個(gè)函數(shù),然后讓用戶(hù)去區(qū)分。
于是我們借助了函數(shù)重載,在每個(gè)類(lèi)的內(nèi)部?jī)?nèi)置一個(gè)work_type,然后根據(jù)每個(gè)類(lèi)的word_type,借助強(qiáng)大的函數(shù)重載機(jī)制,實(shí)現(xiàn)了編譯期的類(lèi)型區(qū)分,也就是編譯期多態(tài)。
代碼如下:
#include <iostream> using namespace std;//兩個(gè)標(biāo)簽類(lèi) struct brain_worker {}; //腦力勞動(dòng) struct physical_worker {}; //體力勞動(dòng)class Worker { public:typedef physical_worker worker_type; };class Farmer { public:typedef physical_worker worker_type; };class Teacher { public:typedef brain_worker worker_type; };class Doctor { public:typedef brain_worker worker_type; };template <typename T> void __distinction(const T &t, brain_worker) {cout << "腦力勞動(dòng)者" << endl; }template <typename T> void __distinction(const T &t, physical_worker) {cout << "體力勞動(dòng)者" << endl; }template <typename T> void distinction(const T &t) {typename T::worker_type _type; //為了實(shí)現(xiàn)重載 __distinction(t, _type); }int main(int argc, char const *argv[]) {Worker w;distinction(w);Farmer f;distinction(f);Teacher t;distinction(t);Doctor d;distinction(d);return 0; }在distinction函數(shù)中,我們先從類(lèi)型中提取出worker_type,然后根據(jù)它的類(lèi)型,選取不同的實(shí)現(xiàn)。
?
問(wèn)題來(lái)了,如果不在類(lèi)中內(nèi)置worker_type,或者有的類(lèi)已經(jīng)寫(xiě)好了,無(wú)法更改了,那么怎么辦?
?
使用Traits
?
我們的解決方案是,借助一種叫做traits的技巧。
我們寫(xiě)一個(gè)模板類(lèi),但是不提供任何實(shí)現(xiàn):
//類(lèi)型traits template <typename T> class TypeTraits;然后我們?yōu)槊總€(gè)類(lèi)型提供一個(gè)模板特化:
//為每個(gè)類(lèi)型提供一個(gè)特化版本 template <> class TypeTraits<Worker> { public:typedef physical_worker worker_type; };template <> class TypeTraits<Farmer> { public:typedef physical_worker worker_type; };template <> class TypeTraits<Teacher> { public:typedef brain_worker worker_type; };template <> class TypeTraits<Doctor> { public:typedef brain_worker worker_type; };然后在distinction函數(shù)中,不再是直接尋找內(nèi)置類(lèi)型,而是通過(guò)traits抽取出來(lái)。
template <typename T> void distinction(const T &t) {//typename T::worker_type _type;typename TypeTraits<T>::worker_type _type;__distinction(t, _type); }?
上面兩種方式的本質(zhì)區(qū)別在于,第一種是在class的內(nèi)部?jī)?nèi)置type,第二種則是在類(lèi)的外部,使用模板特化,class本身對(duì)于type并不知情。
?
兩種方式結(jié)合
?
上面我們實(shí)現(xiàn)了目的,類(lèi)中沒(méi)有work_type時(shí),也可以正常運(yùn)行,但是模板特化相對(duì)于內(nèi)置類(lèi)型,還是麻煩了一些。
于是,我們?nèi)匀皇褂脙?nèi)置類(lèi)型,也仍然使用traits抽取work_type,方法就是為T(mén)ypeTraits提供一個(gè)默認(rèn)實(shí)現(xiàn),默認(rèn)去使用內(nèi)置類(lèi)型,把二者結(jié)合起來(lái)。
這樣我們?nèi)ナ褂肨ypeTraits<T>::worker_type時(shí),有內(nèi)置類(lèi)型的就使用默認(rèn)實(shí)現(xiàn),無(wú)內(nèi)置類(lèi)型的就需要提供特化版本。
class Worker { public:typedef physical_worker worker_type; };class Farmer { public:typedef physical_worker worker_type; };class Teacher { public:typedef brain_worker worker_type; };class Doctor { public:typedef brain_worker worker_type; };//類(lèi)型traits template <typename T> class TypeTraits { public:typedef typename T::worker_type worker_type; };OK,我們現(xiàn)在想添加一個(gè)新的class,于是我們有兩種選擇,
一是在class的內(nèi)部?jī)?nèi)置work_type,通過(guò)traits的默認(rèn)實(shí)現(xiàn)去抽取type。
一種是不內(nèi)置work_type,而是通過(guò)模板的特化,提供work_type。
例如:
class Staff { };template <> class TypeTraits<Staff> { public:typedef brain_worker worker_type; };測(cè)試仍然正常:
Staff s; distinction(s);?
?
進(jìn)一步簡(jiǎn)化
?
這里我們考慮的是內(nèi)置的情形。對(duì)于那些要內(nèi)置type的類(lèi),如果type個(gè)數(shù)過(guò)多,程序編寫(xiě)就容易出現(xiàn)問(wèn)題,我們考慮使用繼承,先定義一個(gè)base類(lèi):
template <typename T> struct type_base {typedef T worker_type; };所有的類(lèi)型,通過(guò)public繼承這個(gè)類(lèi)即可:
class Worker : public type_base<physical_worker> { };class Farmer : public type_base<physical_worker> { };class Teacher : public type_base<brain_worker> { };class Doctor : public type_base<brain_worker> { };?
看到這里,我們應(yīng)該明白,traits相對(duì)于簡(jiǎn)單內(nèi)置類(lèi)型的做法,強(qiáng)大之處在于:如果一個(gè)類(lèi)型無(wú)法內(nèi)置type,那么就可以借助函數(shù)特化,從而借助于traits。而內(nèi)置類(lèi)型僅僅使用于class類(lèi)型。
?
以STL中的迭代器為例,很多情況下我們需要辨別迭代器的類(lèi)型,
例如distance函數(shù)計(jì)算兩個(gè)迭代器的距離,有的迭代器具有隨機(jī)訪(fǎng)問(wèn)能力,如vector,有的則不能,如list,我們計(jì)算兩個(gè)迭代器的距離,就需要先判斷迭代器能否相減,因?yàn)橹挥芯邆潆S機(jī)訪(fǎng)問(wèn)能力的迭代器才具有這個(gè)能力。
我們可以使用內(nèi)置類(lèi)型來(lái)解決。
可是,許多迭代器是使用指針實(shí)現(xiàn)的,指針不是class,無(wú)法內(nèi)置類(lèi)型,于是,STL采用了traits來(lái)辨別迭代器的類(lèi)型。
?
最后,我們應(yīng)該認(rèn)識(shí)到,traits的基石是模板特化。
?
下篇文章,我們使用traits,來(lái)辨別一個(gè)類(lèi)型是否是pod類(lèi)型。
轉(zhuǎn)載于:https://my.oschina.net/inevermore/blog/388694
總結(jié)
以上是生活随笔為你收集整理的模板:什么是Traits的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: TCL 科技:公司在不断扩大 LTPS
- 下一篇: Solr 搭建搜索服务器