概述:
traits是一種特性萃取技術,它在Generic Programming中被普遍運用,經常被用於使不一樣的類型能夠用於相同的操做,或者針對不一樣類型提供不一樣的實現.traits在實現過程當中每每須要用到如下三種C++的基本特性:
enum
typedef
template (partial) specialization
其中:
enum用於將在不一樣類型間變化的標示統一成一個,它在C++中經常被用於在類中替代define,你能夠稱enum爲類中的define;
typedef則用於定義你的模板類支持特性的形式,你的模板類必須以某種形式支持某一特性,不然類型萃取器traits將沒法正常工做.看到這裏你可能會想,太苛刻了吧?其實否則,不支持某種特性自己也是一種支持的方式(見示例2,咱們定義了兩種標示,__xtrue_type和__xfalse_type,分別表示對某特性支持和不支持).
template (partial) specialization被用於提供針對特定類型的正確的或更合適的版本.
藉助以上幾種簡單技術,咱們能夠利用traits提取類中定義的特性,並根據不一樣的特性提供不一樣的實現.你能夠將從特性的定義到萃取,再到traits的實際使用統稱爲traits技術,但這種定義使得traits顯得過於複雜,我更願意將traits的定義限於特性萃取,由於這種定義使得traits顯得更簡單,更易於理解,^_^.
舉例:
上面提到過,traits可被用於針對不一樣類型提供不一樣的實現,那麼下面就舉兩個例子來講明如何實現這一點.
Example 1:
假定咱們須要爲某個類設計一個能夠對全部類型(包括普通的int/long...,提供了clone方法的複雜類型CComplexObject,及由該類派生的類)進行操做的函數clone,下面,先用OO的方法來考慮一下解決方案.看到前面的條件,最早跳進你腦子裏的確定是Interface,pure virtual function等等.對於咱們本身設計的類CComplexObject而言,這不是問題,可是,對於基本數據類型呢?還有那些沒有提供clone方法的複雜類型呢?(這時候你可能會想,要是Java該多easy,全部類都默認從Object派生,而Object已提供了一個默認的clone方法,可是,要使類真正支持clone,還必須implements Cloneable,因此,一樣也不能避免這裏遇到的麻煩).
下面是一個可能的解決方案:
ios
template <typename T, bool isClonable> class XContainer { ... void clone(T* pObj) { if (isClonable) { pObj->clone(); } else { //... non-Clonable algorithm ... } } };
可是隻要你測試一下,這段代碼不能經過編譯.爲何會這樣呢?緣由很簡單:對於沒有實現clone方法的非Clonable類或基本類型,pObj->clone這一句是非法的.
那麼怎樣解決上面的這個難題呢?上面不能經過編譯的代碼告訴咱們,要使咱們的代碼經過編譯,就不能使非Clonable類或基本類型的代碼中出現pObj->clone,即咱們須要針對不一樣類型提供不一樣的實現.爲了實現這一點,咱們能夠在咱們的模板類中用enum定義一個trait,以標示類是否爲Clonable類,而後在原模板類內部引入一個traits提取類Traits,經過對該類進行specilizing,以根據不一樣的trait提供不一樣的實現.具體實現以下:
函數
#include <iostream> using namespace std; class CComplexObject // a demo class { public : void clone() { cout << "in clone" << endl; } }; // Solving the problem of choosing method to call by inner traits class template <typename T, bool isClonable> class XContainer { public: enum { Clonable = isClonable }; void clone(T* pObj) { Traits<isClonable>().clone(pObj); } template <bool flag> class Traits { }; template <> class Traits<true> { public: void clone(T* pObj) { cout << "before cloning Clonable type" << endl; pObj->clone(); cout << "after cloning Clonable type" << endl; } }; template <> class Traits<false> { public: void clone(T* pObj) { cout << "cloning non Clonable type" << endl; } }; }; void main() { int* p1 = 0; CComplexObject* p2 = 0; XContainer<int, false> n1; XContainer<CComplexObject, true> n2; n1.clone(p1); n2.clone(p2); }
編譯運行一下,上面的程序輸出以下的結果:
測試
cloing something non Clonable before doing something Clonable in clone after doing something Clonable
這說明,咱們成功地根據傳入的isClonable模板參數爲模板實例選擇了不一樣的操做,在保證接口相同的狀況下,爲不一樣類型提供了不一樣的實現.
Example 2:
咱們再對上面的例子進行一些限制,假設咱們的clone操做只涉及基本類型和CComplexObject及其派生類,那麼咱們能夠進一步給出下面的解法:
spa
#include <iostream> using namespace std; struct __xtrue_type { }; // define two mark-type struct __xfalse_type { }; class CComplexObject // a demo class { public: virtual void clone() { cout << "in clone" << endl; } }; class CDerivedComplexObject : public CComplexObject // a demo derived class { public: virtual void clone() { cout << "in derived clone" << endl; } }; // A general edtion of Traits template <typename T> struct Traits { typedef __xfalse_type has_clone_method; // trait 1: has clone method or not? All types defaultly has no clone method. }; // Specialized edtion for ComplexObject template <> struct Traits<CComplexObject> { typedef __xtrue_type has_clone_method; }; template <typename T> class XContainer { template <typename flag> class Impl { }; template <> class Impl <__xtrue_type> { public: void clone(T* pObj) { pObj->clone(); } }; template <> class Impl <__xfalse_type> { public: void clone(T* pObj) { } }; public: void clone(T* pObj) { Impl<Traits<T>::has_clone_method>().clone(pObj); } }; void main() { int* p1 = 0; CComplexObject c2; CComplexObject* p2 = &c2; CDerivedComplexObject c3; CComplexObject* p3 = &c3; // you must point to a derived object by a base-class pointer, //it's a little problem XContainer<int> n1; XContainer<CComplexObject> n2; XContainer<CComplexObject> n3; n1.clone(p1); n2.clone(p2); n3.clone(p3); }
如今,全部基本類型以及CComplexObject類系均可以用於XContainer了.
設計