Promotion Trait

泛型程式真是恐怖的技巧 @”@

先來講 Promotion Traits。用過 template 的人大概都覺得只是能夠對任意型別產生對應的函式跟物件, 僅此而已。但若需要判斷兩個型別中, 該用哪一個的時候, 就會變得很麻煩, 需要人工處理, 例如:

template<typenmae T>
T const& max(T const& x, T const& y)
{
return x > y ? x : y;
}

會回傳兩個數值之間較大的一個, 而且是任意型別都可以通吃, 但只限定於型別相同的, 也就是說

max(100, 3)     // 合法, 回傳 100
max(100, 3.0)   // 非法, 無法編譯

雖然看起來非常明顯的是, max(100, 3.0) 的結果應該是 100, 卻需要改用以下方式 work around 而不是依照一般的 coercion 的規則:

max<float>(100, 3.0)
max<int>(100, 3.0)

將指定 max 特化為 float 或是 int 版本的函式。
這倒令人有點喪氣, 似乎沒想像中的方便, 但事實上也不盡然如此 …
實際上我們可以造出類似 template typedef 的 class ,實踐 if … then … else 流程選擇重新決定要使用哪一個型別。
譬如說:

template<bool C, typename Ta, typename Tb>
class IfThenElse;

template<typename Ta, typename Tb>
class IfThenElse<true, Ta, Tb>{
public:
typedef Ta Result;
};

template<typename Ta, typename Tb>
class IfThenElse<false, Ta, Tb>{
public:
typedef Tb Result;
};

我們利用 partial specialization 將 IfThenElse 類別定義兩種情況, 使得當 true 時Result 的型別是第一個型別, 反之則是第二個。

接下來就可以利用這個類別, 定義 Promotion 類別用來幫我們選擇恰當的類別, 首先是用型別的「大小」, 也就用 sizeof() 傳回值來做判斷, 哪個型別能夠包容其他型別,

template<typename T1, typename T2>
class Promotion{
public:
typedef typename
IfThenElse<(sizeof(T1) > sizeof(T2)), // 當 T1 比 T2 大, 則為 T1
T1,
typename IfThenElse<(sizeof(T1) < sizeof(T2)), // 類似
T2,
void
>::Result
>::Result Result;
};

這邊可能有些令人困惑的地方, 例如 T1 的大小等於 T2 時似乎不被加入?通常這時候, T1 跟 T2 根本是一樣的, 要不就是大小一樣, 卻不能完全相容,需要另外指定規則,

template<typename T>
class Promotion<T, T>{
public:
typedef T Result;
};

這段將特化一個相同型別的 Promotion 類別。

接著我們來指定部分基本型別, 轉換的規則, 先另外撰寫一巨集指令, 簡化程式碼:

#define MK_PROMOTION(T1, T2, Tr)                    \
template<> class Promotion<T1, T2> {                 \
public:                                                                          \
typedef Tr Result;                                                  \
};                                                                                   \
template<> class Promotion<T2, T1> {                 \
public:                                                                           \
typedef Tr Result;                                                  \
};

接著就可以用這樣的方式

MK_PROMOTION(int, long, long) // 若型別為 int 跟 long, 則回傳 long
MK_PROMOTION(int, float, float)

加入需要的規則。

回到原本的例子, 動些手腳自動處理需要的型別, 將回傳型別改為 Promotion<Ta, Tb>::Result, 也就是

template<typename Ta, typename Tb>
typename Promotion<Ta, Tb>::Result const& max(Ta const& x, Tb const& y)
{
return x > y ? x : y;
}

這樣就能夠使得 max(3, 100.0) 之類的敘述能夠順利編譯。

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s