c++中利用模板解決條件編譯的問題
問題背景:
唔,我正在寫一個B+樹,其中它的節點尋找演算法需要在不同的應用情境下採用不同的演算法。我可以採用順序尋找和二分法尋找兩種方案,如果這個節點元素比較小時,用順序尋找的方案要快一些,當節點元素比較多時,則用二分法尋找。
第一方案:用if來解決
我們會最容易想到的是
template<int nodesize>
struct CBinaryTreeNode
{
bool Search(...)
{
if(nodesize > critical_size)
{
binary_search(...); //二分法尋找
}
else
{
order_search(...); //順序尋找
}
}
};
OK,這個方案很容易理解,效能也算可以,不過當我們做好第一版後,發現這個模板調用得比較多,還是一個效能瓶頸,必需進一步改善。
另外我們發現,每次都要進行一個容器大小的判斷,事實上容器大小是固定的,(如B+樹中的節點,它是固定的),那麼我們是不是可以把這個判斷去掉呢,我們一定會想到使用臭名昭著的宏定義,使用條件編譯。我從來不反對使用宏,問題是在這裡宏解決不了我們的問題,因為nodesize並不是固定的,而是由其它程式員使用這個類時提供的!
而且它還有一個更嚴重的問題,假如我們在兩種尋找方案中使用了不同的容器(非STL容器),比如較小的時候我們使用數組,而較大的時候我們使用了鏈表。一個只有通過下標運算子來取得元素內容,而另一個只提供了GetAt的方法。那麼我們無法統一使用[]來取得容易中的元素,導致函數無法通過編譯!!!
第二方案:理論上可行的偏特化
嗯,類模板的偏特化正好可以解決這個問題,我們可以如下定義
template<int nodesize,bool flag = (nodesize > critical_size) >
struct CBinaryTreeNode
{
bool Search(...);
};
template<int nodesize>
struct CBinaryTreeNode<nodesize,false>
{
bool Search(...); //順序尋找的實現
};
template<int nodesize>
struct CBinaryTreeNode<nodesize,true>
{
bool Search(...); //二分法尋找的實現
};
嗯,為什麼它是一個理論可行的方案呢,因為特化或者偏特化一個類,你必需全部重新實現模板類所有的函數,否則它們會“缺失”。因此我們不可能採用這種方案。
第三種方案:編譯時間型別機制
我們知道對於一個模板類template<int n>class CTest ,CTest<0> 和 CTest<1>是兩個完全不同的類。另外我們也知道一個函數的重載只跟參數的類型有關,那麼我們就實現這樣的一種方案
template<bool n>
struct bool_trait
{ };
template<int nodesize,bool flag = (nodesize > critical_size) >
struct CBinaryTreeNode
{
bool Search(...)
{
Search(...,bool_trait<flag>());
}
bool Search(..., bool_trait<true> /* */) //二分法尋找
{
}
bool Search(..., bool_trait<false> ) //順序尋找
{
}
};
我們第一方案中兩個問題都解決了,因為在編譯期就會根據nodesize計算出flag,從而決定使用何種尋找方案,另外我們也可以使用完全不同的容器來實現我們的節點,可以在不同的函數中分別使用[]和GetAt來取得對應的元素。c++模板函數的特點是當沒有執行個體化的類或者沒有使用到的函數和方法都不會去進行檢查,因此我們就可以順利地通過編譯了。最方便的是作為庫使用者的程式員,一切都沒有改變,他仍只是和以前一樣
CBinaryTreeNode<100> node;
事實上這種方案也是在Modern c++ design一書中提出來的。很強大的解決方案。當然條條大路通羅馬,我們還可以採用策略編程的方法,不過實在沒有這個方案直觀。