參考博文 https://blog.csdn.net/lihao21/article/details/55043881
Traits classes 的作用主要是用來為使用者提供型別資訊,在 C++ 中,traits 習慣上總是被實作為 struct ,但它們往往被稱為 traits classes,
為了清晰理解 traits 的原理,我們先來看 traits 使用的關鍵技術 —— 模板的特化與偏特化,
模板特化(Template Specialization)
所謂特化,就是將泛型的東西搞得具體化一些,從字面上來解釋,就是為已有的模板引數進行一些使其特殊化的指定,使得以前不受任何約束的模板引數,受到特定的修飾,或完全被指定了下來,
我們先來看下一函式模板的通用定義:
template<typename T>
struct my_is_void {
static const bool value = https://www.cnblogs.com/zhxmdefj/p/false;
};
然后,針對 void 型別,我們有以以下的特化版本:
template<>
struct my_is_void<void> {
static const bool value = https://www.cnblogs.com/zhxmdefj/p/true;
};
測驗代碼如下:
my_is_void<bool> t1;
cout << t1.value << endl; // 輸出0
my_is_void<void> t2;
cout << t2.value << endl; // 輸出1
當宣告 my_is_void<void> t2; 時,使用的是特化版本,故其 value 值為 1,
偏特化(Patial Spcialization)
偏特化就是只指定一部分而非所有模板引數,或者是引數的一部分而非全部特性,(模板函式只能全特化,沒有偏特化)
template<typename T>
struct my_is_pointer {
static const bool value = https://www.cnblogs.com/zhxmdefj/p/false;
};
我們對模板引數T進行限制,要求其為一個指標的型別:
template<typename T>
struct my_is_pointer<T*> {
static const bool value = https://www.cnblogs.com/zhxmdefj/p/true;
};
測驗:
my_is_pointer<int> p1;
cout << p1.value << endl; // 輸出 0,使用原始模板
my_is_pointer<int*> p2;
cout << p2.value << endl; // 輸出 1,使偏特化模板,因為指定了 int * 型別的引數
typename
以下模板的宣告中, class 和 typename 有什么不同?
template<class T> class Test;
template<typename T> class Test;
答案是沒有不同,但除此之外,C++ 并不總是把 class 和 typename 視為等價,有時候我們一定得使用 typename,
默認情況下,C++ 語言假定通過作用域運算子訪問的名字不是型別,因此,如果你希望使用一個模板型別引數的型別成員,就必須顯式告訴編譯器該名字是一個型別,我們通過使用關鍵字 typename 來實作這一點:
template<typename T>
typename T::value_type top(const T &c)
{
if (!c.empty())
return c.back();
else
return typename T::value_type();//必須加typename
}
top 函式期待一個容器型別的實參,它使用 typename 指明其回傳型別
測驗代碼:
vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
cout << top<vector<int> >(vec) << endl; // 輸出3
當我們希望通知編譯器一個名字表示型別時,必須使用關鍵字 typename,而不能使用 class,
Traits Classes
說完了背景知識,我們正式進入 traits
我們知道,在 STL 中,容器與演算法是分開的,彼此獨立設計,容器與演算法之間通過迭代器聯系在一起,那么,演算法是如何從迭代器類中萃取出容器元素的型別的?沒錯,這正是我們要說的 traits classes 的功能,
迭代器所指物件的型別,稱為該迭代器的 value_type,我們來簡單模擬一個迭代器 traits classes 的實作,
template<class IterT>
struct my_iterator_traits {
typedef typename IterT::value_type value_type;
};
my_iterator_traits 其實就是個類模板,其中包含一個型別的宣告,
對于my_iterator_traits,我們再宣告一個偏特化版本,
template<class IterT>
struct my_iterator_traits<IterT*> {
typedef IterT value_type;
//即如果 my_iterator_traits 的實參為指標型別時,
//直接使用指標所指元素型別作為 value_type,
};
為了測驗 my_iterator_traits 能否正確萃取迭代器元素的型別,我們先撰寫以下的測驗函式,
void fun(int a) {
cout << "fun(int) is called" << endl;
}
void fun(double a) {
cout << "fun(double) is called" << endl;
}
void fun(char a) {
cout << "fun(char) is called" << endl;
}
我們通過函式多載的方式,來測驗元素的型別,
測驗代碼如下:
my_iterator_traits<vector<int>::iterator>::value_type a;
fun(a); // 輸出 fun(int) is called
my_iterator_traits<vector<double>::iterator>::value_type b;
fun(b); // 輸出 fun(double) is called
my_iterator_traits<char*>::value_type c;
fun(c); // 輸出 fun(char) is called
為了便于理解,我們這里貼出 vector 迭代器宣告代碼的簡化版本:
template <class T, ...>
class vector {
public:
class iterator {
public:
typedef T value_type;
...
};
...
};
我們來解釋 my_iterator_traits<vector<int>::iterator>::value_type a; 陳述句的含義:
vector<int>::iterator 為 vector<int> 的迭代器,該迭代器包含了 value_type 的宣告,由 vector 的代碼可以知道該迭代器的 value_type 即為 int 型別,
接著,my_iterator_traits<vector<int>::iterator> 會采用 my_iterator_traits 的通用版本,即 my_iterator_traits<vector<int>::iterator>::value_type 使用 typename IterT::value_type 這一型別宣告,這里 IterT 為 vector<int>::iterator,故整個陳述句萃取出來的型別為 int 型別,
對 double 型別的 vector 迭代器的萃取也是類似的程序,
而 my_iterator_traits<char*>::value_type 則使用 my_iterator_traits 的偏特化版本,直接回傳 char 型別,
由此看來,通過 my_iterator_traits ,我們正確萃取出了迭代器所指元素的型別,
總結一下我們設計并實作一個 traits class 的程序:
- 確認若干我們希望將來可取得的型別相關資訊,例如,對于上面的迭代器,我們希望取得迭代器所指元素的型別;
- 為該資訊選擇一個名稱,例如,上面我們起名為 value_type;
- 提供一個 template 和一組特化版本(例如,我們上面的 my_iterator_traits),內容包含我們希望支持的型別相關資訊,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/52940.html
標籤:C++
上一篇:前端開發工程師前景怎么樣?
