模板:什么是Traits
Traits不是一種語法特性,而是一種模板編程技巧。Traits在C++標準庫,尤其是STL中,有著不可替代的作用。
如何在編譯期間區分類型
下面我們看一個實例,有四個類,Farm、Worker、Teacher和Doctor,我們需要區分他們是腦力勞動者還是體力勞動者。以便于做出不同的行動。
這里的問題在于,我們需要為兩種類型提供一個統一的接口,但是對于不同的類型,必須做出不同的實現。
我們不希望寫兩個函數,然后讓用戶去區分。
于是我們借助了函數重載,在每個類的內部內置一個work_type,然后根據每個類的word_type,借助強大的函數重載機制,實現了編譯期的類型區分,也就是編譯期多態。
代碼如下:
#include <iostream>
using namespace std;
//兩個標簽類
struct brain_worker {}; //腦力勞動
struct physical_worker {}; //體力勞動
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 << "腦力勞動者" << endl;
}
template <typename T>
void __distinction(const T &t, physical_worker)
{
cout << "體力勞動者" << endl;
}
template <typename T>
void distinction(const T &t)
{
typename T::worker_type _type; //為了實現重載
__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函數中,我們先從類型中提取出worker_type,然后根據它的類型,選取不同的實現。
問題來了,如果不在類中內置worker_type,或者有的類已經寫好了,無法更改了,那么怎么辦?
使用Traits
我們的解決方案是,借助一種叫做traits的技巧。
我們寫一個模板類,但是不提供任何實現:
//類型traits template <typename T> class TypeTraits;
然后我們為每個類型提供一個模板特化:
//為每個類型提供一個特化版本
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函數中,不再是直接尋找內置類型,而是通過traits抽取出來。
template <typename T>
void distinction(const T &t)
{
//typename T::worker_type _type;
typename TypeTraits<T>::worker_type _type;
__distinction(t, _type);
}
上面兩種方式的本質區別在于,第一種是在class的內部內置type,第二種則是在類的外部,使用模板特化,class本身對于type并不知情。
兩種方式結合
上面我們實現了目的,類中沒有work_type時,也可以正常運行,但是模板特化相對于內置類型,還是麻煩了一些。
于是,我們仍然使用內置類型,也仍然使用traits抽取work_type,方法就是為TypeTraits提供一個默認實現,默認去使用內置類型,把二者結合起來。
這樣我們去使用TypeTraits<T>::worker_type時,有內置類型的就使用默認實現,無內置類型的就需要提供特化版本。
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;
};
//類型traits
template <typename T>
class TypeTraits
{
public:
typedef typename T::worker_type worker_type;
};
OK,我們現在想添加一個新的class,于是我們有兩種選擇,
一是在class的內部內置work_type,通過traits的默認實現去抽取type。
一種是不內置work_type,而是通過模板的特化,提供work_type。
例如:
class Staff
{
};
template <>
class TypeTraits<Staff>
{
public:
typedef brain_worker worker_type;
};
測試仍然正常:
Staff s; distinction(s);
進一步簡化
這里我們考慮的是內置的情形。對于那些要內置type的類,如果type個數過多,程序編寫就容易出現問題,我們考慮使用繼承,先定義一個base類:
template <typename T>
struct type_base
{
typedef T worker_type;
};
所有的類型,通過public繼承這個類即可:
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>
{
};
看到這里,我們應該明白,traits相對于簡單內置類型的做法,強大之處在于:如果一個類型無法內置type,那么就可以借助函數特化,從而借助于traits。而內置類型僅僅使用于class類型。
以STL中的迭代器為例,很多情況下我們需要辨別迭代器的類型,
例如distance函數計算兩個迭代器的距離,有的迭代器具有隨機訪問能力,如vector,有的則不能,如list,我們計算兩個迭代器的距離,就需要先判斷迭代器能否相減,因為只有具備隨機訪問能力的迭代器才具有這個能力。
我們可以使用內置類型來解決。
可是,許多迭代器是使用指針實現的,指針不是class,無法內置類型,于是,STL采用了traits來辨別迭代器的類型。
最后,我們應該認識到,traits的基石是模板特化。
下篇文章,我們使用traits,來辨別一個類型是否是pod類型。
總結
以上是生活随笔為你收集整理的模板:什么是Traits的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 美体内衣(真美内衣多少钱一套)
- 下一篇: uni-app 点击查看全部,再点击隐藏