traits:Traits技術初探
阿新 • • 發佈:2018-12-27
概述:
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,所以,同樣也不能避免這裡遇到的麻煩).下面是一個可能的解決方案:
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);
}
編譯執行一下,上面的程式輸出如下的結果:
doing something non Clonable
before doing something Clonable
in clone
after doing something Clonable
這說明,我們成功地根據傳入的isClonable模板引數為模板例項選擇了不同的操作,在保證介面相同的情況下,為不同型別提供了不同的實現.
Example 2:
我們再對上面的例子進行一些限制,假設我們的clone操作只涉及基本型別和CComplexObject及其派生類,那麼我們可以進一步給出下面的解法:
#include <iostream>
using namespace std;
struct __xtrue_type { }; // define two mark-typestruct __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了.結語:看到這裡,你或許會說,traits不過如此,還以為是什麼高深的玩意呢!其實技術就是這樣,說白了都很Easy,關鍵是怎麼將他們用於實際,為實際的Designing/Development服務.畢竟,在IT領域,不能應用於實際的技術是沒有價值的.
traits是一種特性萃取技術,它在Generic Programming中被廣泛運用,常常被用於使不同的型別可以用於相同的操作,或者針對不同型別提供不同的實現.traits在實現過程中往往需要用到以下三種C++的基本特性:
enum
typedef
template (partial) specialization
其中:
enum用於將在不同型別間變化的標示統一成一個,它在C++中常常被用於在類中替代define,你可以稱enum為類中的define;
typedef則用於定義你的模板類支援特性的形式,你的模板類必須以某種形式支援某一特性,否則型別萃取器traits將無法正常工作.看到這裡你可能會想,
template (partial) specialization被用於提供針對特定型別的正確的或更合適的版本.藉助以上幾種簡單技術,我們可以利用traits提取類中定義的特性,並根據不同的特性提供不同的實現.你可以將從特性的定義到萃取,再到traits的實際使用統稱為traits技術,但這種定義使得traits顯得過於複雜,我更願意將traits的定義限於特性萃取,因為這種定義使得traits顯得更簡單,更易於理解,^
舉例:上面提到過,traits可被用於針對不同型別提供不同的實現,那麼下面就舉兩個例子來說明如何實現這一點.
Example 1:
假定我們需要為某個類設計一個可以對所有型別(包括普通的int/long...,提供了clone方法的複雜型別CComplexObject,及由該類派生的類)進行操作的函式clone,下面,先用OO的方法來考慮一下解決方案.看到前面的條件,最先跳進你腦子裡的肯定是Interface,pure virtual function等等.對於我們自己設計的類CComplexObject而言,這不是問題,但是,對於基本資料型別呢?還有那些沒有提供clone方法的複雜型別呢?(
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);
}
編譯執行一下,上面的程式輸出如下的結果:
doing something non Clonable
before doing something Clonable
in clone
after doing something Clonable
這說明,我們成功地根據傳入的isClonable模板引數為模板例項選擇了不同的操作,在保證介面相同的情況下,為不同型別提供了不同的實現.
Example 2:
我們再對上面的例子進行一些限制,假設我們的clone操作只涉及基本型別和CComplexObject及其派生類,那麼我們可以進一步給出下面的解法:
#include <iostream>
using namespace std;
struct __xtrue_type { }; // define two mark-typestruct __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了.結語:看到這裡,你或許會說,traits不過如此,還以為是什麼高深的玩意呢!其實技術就是這樣,說白了都很Easy,關鍵是怎麼將他們用於實際,為實際的Designing/Development服務.畢竟,在IT領域,不能應用於實際的技術是沒有價值的.