C++對模板的具現(xiàn)化采用盡量惰性的原則。只有當你使用了模板的內(nèi)部定義,編譯器才會為模板生成對應(yīng)的定義。
所以對于元函數(shù),當你不訪問內(nèi)部的Result對其求值,編譯器是不會為其做計算的。因此我們可以把一個元函數(shù)當做運行期函數(shù)指針一樣進行傳遞,直到我們需要的時候再對其求值。
對于惰性,我們來看下面這個例子:
template<typename T, bool isClonable>
struct Creator
{
static T* create(const T* instance)
{
if(isClonable)
{
return instance->clone();
}
else
{
return new T(*instance);
}
}
};
如上我們希望有一個工廠類,用來創(chuàng)建入?yún)類型的對象。如果T支持clone方法,則采用從一個現(xiàn)有對象clone出新對象,否則調(diào)用拷貝構(gòu)造函數(shù)new出來一個新對象。Creator的第二個參數(shù)isClonable用來指示前一個參數(shù)T是否支持clone。
遺憾的是,上述代碼是不能工作的。當我們傳遞一個不支持clone的類形T進去,即使我們將isClonable設(shè)為false,編譯器也會對create函數(shù)進行完整編譯,會報告T缺少clone方法。
struct UnclonableObject
{
UnclonableObject(){}
UnclonableObject(const UnclonableObject&) {}
};
Creator<UnclonableObject, false> creator;
然而當我們寫出上述代碼進行編譯,發(fā)現(xiàn)卻能編譯通過!原因是C++編譯器的惰性特征做了手腳,此時它沒有看到任何人調(diào)用Creator<UnclonableObject, false>的create方法,所以并未生成該方法。
一旦我們寫出如下代碼,就會和我們最初預想的一致:編譯失敗,編譯器告訴我們UnclonableObject中沒有clone方法。
UnclonableObject object;
UnclonableObject* newObject = creator.create(&object);
解決該問題的方式很簡單,就是把使用if做運行期分支選擇的實現(xiàn)轉(zhuǎn)換成使用編譯期的分支選擇元函數(shù)__if()來實現(xiàn)。
template<typename T>
struct ClonableCreator
{
static T* create(const T* instance)
{
return instance->clone();
}
};
template<typename T>
struct UnclonableCreator
{
static T* create(const T* instance)
{
return new T(*instance);
}
};
template<typename T, bool isClonable> using Creator = __if(__bool(isClonable), ClonableCreator<T>, UnclonableCreator<T>);
由于模板元編程的惰性特征,__if()元函數(shù)會根據(jù)第一個入?yún)⒌腷ool值,對后面的兩個參數(shù)中的一個進行求值。因此當我們調(diào)用Creator<UnclonableObject, false>時,__if(__bool(false), ClonableCreator<T>, UnclonableCreator<T>)只會對UnclonableCreator<T>具現(xiàn)化,所以沒有再出現(xiàn)之前的編譯錯誤。
利于惰性求值,在編譯期選擇性的做類型具現(xiàn)化,是模板元編程非常有用的特性之一。