您好,登錄后才能下訂單哦!
這篇文章主要介紹了C++中traits技術(shù)的示例分析,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
讓我們一點(diǎn)點(diǎn)拋出問(wèn)題,然后一點(diǎn)點(diǎn)深入。
1. 首先,在算法中運(yùn)用迭代器時(shí),很可能會(huì)用到其相應(yīng)型別(迭代器所指之物的型別)。假設(shè)算法中有必要聲明一個(gè)變量,以“迭代器所指對(duì)象的型別”為型別,該怎么辦呢?
解決方法是:利用function template的參數(shù)推導(dǎo)機(jī)制。
template <class I, class T> void func_impl(I iter, T t) { T tmp; // 這里就是迭代器所指物的類型新建的對(duì)象 // ... 功能實(shí)現(xiàn) } template <class I> inline void func(I iter) { func_impl(iter, *iter); // 傳入iter和iter所指的值,class自動(dòng)推導(dǎo) } int main() { int i; func(&i); }
這里已經(jīng)可以看出封裝的意思了,沒(méi)有一層impl的封裝的話,每次你都要顯式地說(shuō)明迭代器指向?qū)ο笮蛣e,才能新建tmp變量。加一層封裝顯得清爽很多。
迭代器相應(yīng)型別不只是“迭代器所指對(duì)象的型別”一種而已。根據(jù)經(jīng)驗(yàn),最常用的相應(yīng)型別有五種,然而并非任何情況下任何一種都可以利用上述的template參數(shù)推導(dǎo)機(jī)制來(lái)取得。
函數(shù)的“template參數(shù)推導(dǎo)機(jī)制”推導(dǎo)的只是參數(shù),無(wú)法推導(dǎo)函數(shù)的返回值類型。萬(wàn)一需要推導(dǎo)函數(shù)的傳回值,就無(wú)能為力了。
2. 聲明內(nèi)嵌型別似乎是個(gè)好主意,這樣我們就可以直接獲取。
template <class T> struct MyIter { typedef T value_type; // 內(nèi)嵌型別聲明 // ... }; template <class I> typename I::value_type func(I ite) { return *ite; } // ... MyIter<int> ite(new int(8)); cout << func(ite);
看起來(lái)不錯(cuò),但是并不是所有迭代器都是class type,原生指針就不行!如果不是class type,就無(wú)法為它定義內(nèi)嵌型別。
這時(shí)候就需要 偏特化 出現(xiàn)。
3. 偏特化就是在特化的基礎(chǔ)上再加一點(diǎn)限制,但它還是特化的template。
template <class I> struct iterator_traits { typedef typename I::value_type value_type; }; template <class I> struct iterator_traits<T*> { typedef T value_type; }; template <class I>12 typename iterator_traits<I>::value_type func(I ite) { return *ite; }
func在調(diào)用 I 的時(shí)候,首先把 I 傳到萃取器中,然后萃取器就匹配最適合的 value_type。(萃取器會(huì)先匹配最特別的版本)這樣當(dāng)你傳進(jìn)一個(gè)原生指針的時(shí)候,首先匹配的是帶<T*>的偏特化版本,這樣 value_type 就是 T,而不是沒(méi)有事先聲明的 I::value_type。這樣返回值就可以使用 typename iterator_traits<I>::value_type 來(lái)知道返回類型。
下面附上《STL源碼剖析》的圖片:
迭代器有常見(jiàn)有五種類型: value_type, difference_type, reference_type, pointer_type都比較容易在 traits 和 相應(yīng)偏特化中提取。但是,iterator_category一般也有5個(gè),這個(gè)相應(yīng)型別會(huì)引發(fā)較大規(guī)模的寫代碼工程。
例如,我們實(shí)現(xiàn)了 func_II, func_BI, func_RAI 分別代表迭代器類型是Input Iterator,Bidirectional Iterator和Random Access Iterator的對(duì)應(yīng)實(shí)現(xiàn)。
現(xiàn)在,當(dāng)客端調(diào)用func()的時(shí)候,我們可能需要做一個(gè)判斷:
template<class Iterator> void func(Iterator& i) { if (is_random_access_iterator(i)) func_RAI(i); if (is_bidirectional_iterator(i)) func_BI(i); else func_II(i); }
但這樣在執(zhí)行時(shí)期才決定使用哪一個(gè)版本,會(huì)影響程序效率。最好能夠在編譯期就選擇正確的版本。
重載這個(gè)函數(shù)機(jī)制可以達(dá)成這個(gè)目標(biāo)。
struct input_iterator_tag {}; struct output_iterator_tag {}; struct forward_iterator_tag : public input_iterator_tag {}; // ... // 繼承的好處就是,當(dāng)函數(shù)需要用 input_iterator_tag 的時(shí)候 // 假設(shè)你傳進(jìn)一個(gè)forward_iterator_tag,它會(huì)沿繼承向上找,知道符合條件
聲明了一些列 tag 之后,我們就可以重載 func函數(shù): func(tag)。
到這里,各個(gè)型別的具體重載實(shí)現(xiàn)已經(jīng)寫好,但是需要一個(gè)統(tǒng)一的接口,這時(shí)候 traits 就可以出場(chǎng)了。
template<class Iterator> inline void func(Iterator& i) { typedef typename Iterator_traits<Iterator>::iterator_category category; __func(i, category()); // 各型別的重載 }
簡(jiǎn)單實(shí)例代碼
所以說(shuō),traits一方面,在面對(duì)不同的輸入類時(shí),能找到合適的返回型別;另一方面,當(dāng)型別對(duì)應(yīng)有不同的實(shí)現(xiàn)函數(shù)的時(shí)候,能起到一個(gè)提取型別然后分流的作用。
先假設(shè)我們有一個(gè) func 函數(shù),可以接受 自定義的類 或者 原始的指針 作為參數(shù),并自動(dòng)輸出使用了什么tag。
首先根據(jù) traits(由本身或偏特化版本實(shí)現(xiàn)) ,它會(huì)提取 u 的返回型別,然后調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù) return_type(), 來(lái)當(dāng)作各個(gè)重載版本 __func 的重載標(biāo)志區(qū)分不同的實(shí)際函數(shù)。
首先我們看看接口代碼的編寫
template <class unknown_class> inline typename unknown_class_traits<unknown_class>::return_type // 萃取器取得對(duì)應(yīng)型別 func(unknown_class u) { typedef typename unknown_class_traits<unknown_class>::return_type return_type; return __func(u, return_type()); // 需要調(diào)用構(gòu)造函數(shù)當(dāng)tag }
然后是實(shí)現(xiàn)設(shè)定的 tag ,用來(lái)模仿前面說(shuō)的 II,RAI等
template <class unknown_class> inline typename unknown_class_traits<unknown_class>::return_type return_type(unknown_class) { typedef typename unknown_class_traits<unknown_class>::return_type RT; return RT(); }
有了這些我們就可以測(cè)試了
struct A {}; struct B : A{};
然后是 traits 隆重登場(chǎng),有兩個(gè)偏特化版本。
/*特性萃取器*/ template <class unknown_class> struct unknown_class_traits { typedef typename unknown_class::return_type return_type; }; /*特性萃取器 —— 針對(duì)原生指針*/ template <class T> struct unknown_class_traits<T*> { typedef T return_type; }; /*特性萃取其 —— 針對(duì)指向常數(shù)*/ template <class T> struct unknown_class_traits<const T*> { typedef const T return_type; };
突然忘記了交代 unknown_class 的結(jié)構(gòu),自定義的類,必須要 typedef。
template <class AorB> struct unknown_class { typedef AorB return_type; };
最后是func各個(gè)重載版本。
template <class unknown_class> inline typename unknown_class_traits<unknown_class>::return_type __func(unknown_class, A) { cout << "use A flag" << endl; return A(); } template <class unknown_class> inline typename unknown_class_traits<unknown_class>::return_type __func(unknown_class, B) { cout << "use B flag" << endl; return B(); } template <class unknown_class, class T> T __func(unknown_class, T) { cout << "use origin ptr" << endl; return T(); }
有了這些我們就可以測(cè)試了
int main() { unknown_class<B> b; unknown_class<A> a; //unknown_class<int> i; int value = 1; int *p = &value; A v1 = func(a); B v2 = func(b); int v3 = func(p); char ch = getchar(); }
可以看到,對(duì)于用自定義類傳入同一個(gè)接口,它會(huì)自動(dòng)使用對(duì)應(yīng)的函數(shù),而且返回值也合適。對(duì)原始指針也適用,完美!
附
下面是完整代碼:
#include <iostream> using namespace std; /*先定義一些tag*/ struct A {}; struct B : A{}; // 繼承的好處就是,當(dāng)函數(shù)需要參數(shù)為A, // 而你傳入的參數(shù)為B的時(shí)候,可以往上一直找到適合的對(duì)象 /*假設(shè)有一個(gè)未知類*/ template <class AorB> struct unknown_class { typedef AorB return_type; }; /*特性萃取器*/ template <class unknown_class> struct unknown_class_traits { typedef typename unknown_class::return_type return_type; }; /*特性萃取器 —— 針對(duì)原生指針*/ template <class T> struct unknown_class_traits<T*> { typedef T return_type; }; /*特性萃取其 —— 針對(duì)指向常數(shù)*/ template <class T> struct unknown_class_traits<const T*> { typedef const T return_type; }; /*決定使用哪一個(gè)類型*/ template <class unknown_class> inline typename unknown_class_traits<unknown_class>::return_type return_type(unknown_class) { typedef typename unknown_class_traits<unknown_class>::return_type RT; return RT(); } template <class unknown_class> inline typename unknown_class_traits<unknown_class>::return_type __func(unknown_class, A) { cout << "use A flag" << endl; return A(); } template <class unknown_class> inline typename unknown_class_traits<unknown_class>::return_type __func(unknown_class, B) { cout << "use B flag" << endl; return B(); } template <class unknown_class, class T> T __func(unknown_class, T) { cout << "use origin ptr" << endl; return T(); } template <class unknown_class> inline typename unknown_class_traits<unknown_class>::return_type func(unknown_class u) { typedef typename unknown_class_traits<unknown_class>::return_type return_type; return __func(u, return_type()); } int main() { unknown_class<B> b; unknown_class<A> a; //unknown_class<int> i; int value = 1; int *p = &value; A v1 = func(a); B v2 = func(b); int v3 = func(p); char ch = getchar(); }
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“C++中traits技術(shù)的示例分析”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。