在編寫C++類模板時,有時希望模板參數滿足某種要求,即約束條件,比如要求某個模板型別參數T必須派生於基類TBase。
在C#泛型中可以通過where關鍵字來指定約束條件,而C++模板則缺乏類似的約束機制。
C++0x中引入了一些新的特性,C++標準庫也得到了改進和擴充,有兩篇不錯的參考文章:
Explicating the new C++ standard (C++0x), and its implementation in VC10Standard C++ Library changes in Visual C++ 2010
利用這些新特性,可以為模板參數指定一些約束。
比如,WTL中的模板,其中的一些模板參數必須從指定類繼承(直接或間接繼承),才能編譯通過,如CApplicationT類:
template<typename T, typename TMainWindow, bool bInitializeCom = false>
class CApplicationT :
public CAppModule
{public:
//Call this method in the application's entry point[_tWinMain(...)]
int WinMain(HINSTANCE hInstance,LPTSTR lpCmdLine,int nCmdShow)
{ //Ensure that TMainWindow is derived from CWindow
static_assert(std::is_base_of<CWindow,TMainWindow>::value,
"TMainWindow needs to be CWindow based.");
UNREFERENCED_PARAMETER(lpCmdLine);
T* pT = static_cast<T*>(this);
//......
int nRet = 0;
{ CMessageLoop msgLoop;
AddMessageLoop(&msgLoop);
TMainWindow wndMain;
_g_hMainWindow = wndMain.Create(NULL,CWindow::rcDefault);
if(_g_hMainWindow == NULL)
{ ATLTRACE(_T("Failed to create Main window!\n")); return 0;
}
wndMain.ShowWindow(nCmdShow);
wndMain.UpdateWindow();
wndMain.CenterWindow();
//......
這個模板類有個型別參數TMainWindow,實現代碼中調用了這個類的一些成員函數,如Create(…),ShowWindow()等等。然而,這些成員函數實際上是CWindow類的成員函數。也就是說,TMainWindow類必須從CWindow類繼承(直接或間接繼承),才能在模板執行個體化時正確編譯通過。
否則,如果TMainWindow不是CWindow的衍生類別,在編譯如下代碼時:
CApplication<CTestWindow> g_MainApp;
編譯器會提示如下錯誤:
e:\coding\mywtllib\wtlapp.h(56): error C2039: 'Create' : is not a member of 'CTestWindow'
d:\cpp\wtl\wtlapp\win7shell\mainapp.cpp(6) : see declaration of 'CTestWindow'
e:\coding\mywtllib\wtlapp.h(62): error C2039: 'ShowWindow' : is not a member of 'CTestWindow'
d:\cpp\wtl\wtlapp\win7shell\mainapp.cpp(6) : see declaration of 'CTestWindow'
這種編譯錯誤不夠直觀,當碰到某些不太常見的成員函數時,特別是使用別人編寫的模板代碼,又缺少文檔說明時,很難搞清楚TMainWindow應當滿足什麼條件才能編譯通過。
幸好,我們現在可以利用C++0x的一些新特性來顯式指定TMainWindow必須從CWindow類繼承(直接或間接繼承),方法為在CApplicationT模板實現代碼中添加一段靜態斷言代碼:
template<typename T, typename TMainWindow, bool bInitializeCom = false>
class CApplicationT :
public CAppModule
{public:
//Call this method in the application's entry point[_tWinMain(...)]
int WinMain(HINSTANCE hInstance,LPTSTR lpCmdLine,int nCmdShow)
{ //Ensure that TMainWindow is derived from CWindow
static_assert(std::is_base_of<CWindow,TMainWindow>::value,
"TMainWindow needs to be CWindow based.");
UNREFERENCED_PARAMETER(lpCmdLine);
T* pT = static_cast<T*>(this);
//......
這樣,當TMainWindow不滿足條件時,編譯器就會提示如下錯誤:
e:\coding\mywtllib\wtlapp.h(25): error C2338: TMainWindow needs to be CWindow based.
這樣,錯誤原因就清晰多了。
使用std::is_base_of<>時,需要添加標頭檔”#include <type_traits>”