2.3 類别範本的區域性使用 Partial Usage of Class Templates
類别範本通常在它例項化的模板實參上進行多種操作(包括構造和析構),這給人一種印象:模板實參必須提供所有類别範本的所有成員函式的所有操作。但是事實並非如此:模板實參僅需提供必要的操作而非可能需要的操作。
比如說,如果類Stack<>
提供一個成員函式printOn()
用於列印整個stack的內容,並對每個元素呼叫operator<<
:
template <typename T>
class Stack
{
...
void printOn(std::ostream& strm) const
{
for(T const& elem: elems)
{
strm << elem << ' '; //每個元素呼叫<<
}
}
}
但這依然可以使用沒有定義operator<<
的類作為該類别範本的模板實參:
Stack<std::pair<int,int>> ps; //注意: std::pair<>沒有定義operator<<
ps.push({4, 5}); //OK
ps.push({6,7}); //OK
std::cout << ps.top().first << '\n'; //OK
std::cout << ps.top().second << '\n'; //OK
只有當呼叫這樣的stack的printOn()
方法時,該程式碼才會生成錯誤,因為它不能例項化對該特殊型別的operator<<
的呼叫:
ps.printOn(std::cout); //ERROR: 元素型別不支援operator<<
2.3.1 Concepts
這引發了一個問題,如何知道哪些是模板例項化所需要的操作?術語概念(Concept)用於指示約束條件的集合,並在模板庫中重複使用。比如,C++標準庫依賴於隨機訪問迭代器(random access iterator)和預設構造(default constructible)。
當前(C++17),concepts只能或多或少地在文件中進行表述(比如程式碼註釋)。這可能是個重要的問題,因為未遵循約束條件的錯誤可能引起噁心的錯誤訊息(詳見第9.4節)。
許多年來,有許多方法和試驗來支援concepts的定義和驗證,將其作為語言特性。然而,截止C++17還沒有標準化的方法。
自從C++11以後,至少可以通過使用static_assert
關鍵字和預定義的型別特性來檢查基本的約束條件,比如:
template <typename T>
class C
{
static_assert(std::is_default_constructible<T>::value, "Class C requires default-constructible elements");
...
}
沒有該斷言,如果需要預設建構函式,編譯依然會失敗。然而,該錯誤資訊可能包含整個模板例項化的歷史,從開始例項化到真實的模板定義(即錯誤檢測到的地方,詳見9.4節)。
然而,更多複雜的程式碼需要檢查,比如,型別T的物件提供一種特殊的成員函式或者他們可以使用操作<進行比較。詳見19.6.3節的一個詳細的程式碼例程。
參考附件E關於更多的有關C++ Concept的討論。