2.2 使用Stack類别範本 Use of Class Template Stack
在C++17以前,使用類别範本必須顯式指定模板實參[1]。以下例子展示如何使用類别範本Stack<>
// basics/stack1test.cpp
#include "stack1.hpp"
#include <iostream>
#include <string>
int main()
{
Stack<int> intStack; //int型別的棧
Stack<std::string> stringStack; //string型別的棧
//操作int型別的棧
intStack.push(7);
std::cout << intStack.top() << '\n';
//操作string型別的棧
stdString.push("hello");
std::cout << stringStack.top() << '\n';
stringStack.pop();
}
通過宣告型別Stack
注意,只有被呼叫的模板(成員)函式程式碼才能被例項化。對於類别範本,成員函式只有被呼叫才會例項化。這節省了時間和空間,同時也使得類别範本可以被部分使用,這將在第2.3節中討論。
本例中,int型別和string型別的預設建構函式、push()
函式、top()
函式都將被例項化,然而pop()
函式只有string型別的進行了例項化。如果類别範本有靜態成員,這些靜態成員也只例項化一次,而且只有使用了類别範本的那一種型別進行了例項化。
例項化後的類别範本型別可以像其他型別一樣使用,可以用const或者volatile進行限定,或者基於它衍生出陣列和引用。也可以將其作為typedef或者using進行型別定義的一部分(更多型別定義的內容詳見第2.8節),或者當構建其他模板型別時作為型別引數,比如:
void foo(Stack<int> const& s) //引數s是int的Stack
{
using IntStack = Stack<int>; //IntStack是Stack<int>的別名
Stack<int> istack[10]; //istack是長度為10的Stack<int>的陣列
IntStack istack2[10]; //istack2也是長度為10的Stack<int>的陣列
}
模板實參可以是任何型別,比如float型別的指標,甚至是int的Stack:
Stack<float*> floatPtrStack; //float指標的Stack
Stack<Stack<int>> intStackStack; //int的Stack的Stack
唯一的要求便是任何被呼叫的操作對於該型別是可行的。
注意到,C++11之前必須在兩個閉模板括號(closing template brackets)之間放置空格:
Stack<Stack<int> > intStackStack; //任何C++版本都沒問題
如果不這麼做而使用符號 >>,這將導致語法錯誤:
Stack<Stack<int>> intStackStack; //C++11之前將引發錯誤
舊版本的這種行為的原因是這可以幫助C++編譯器在第一輪中將原始碼分成獨立語義的片段(tokenize the source code independent of the semantics of the code)。然而,由於缺失空格是個典型的bug,這需要對應的錯誤訊息,該程式碼的語義將越來越難以考慮在內。因此C++11移除了“在兩個閉合模板括號中間必須加入空格”的規則,史稱“角括號hack”(詳見13.3.1節)。
腳註
C++17引入了類别範本實參推斷,這使得可以跳過指定模板實參,只要可以從建構函式推斷出模板實參。這將在2.9節中詳細討論。 ↩︎