摘要:
1)讲具体类型当做template arguements传入才能使用class template。于是该class template便以你所指定的那些类型,由编译器实例化并编译;
2)class template之中,只有被实际调用的成员函数,才能被实例化;
3)针对某些特定类型,可以针对class template进行全特化和偏特化;
4)可以为template parameters定义默认值,即预设模板变量值。
**
实作class template stack
**
#include <vector>
#include <stdexcept>
template <typename T>
class Stacck{
private:
std::vector<T> elems;
public:
void push(T const&);
void pop();
T top() const;
bool empty() const{
return elems.empty();
}
};
template <typename T>
void Stack<T>::push(T const& elem){
elems.push_back(elem);
}
template <typename T>
void Stack<T>::pop(){
if(elems.empty(){
throw std::out_of_range("Stack<>::pop: empty stack");
}
T elem=elems.back();
elems.pop_back();
return elem;
}
template <typename T>
T Stack<T>::top() const{
if(elems.empty()){
throw std::out_of_range("Stak<>::top empty stack");
}
return elems.back();
}
这个class template是以标准库的class template vector<>为基础构建起来的。这里的typename也可以用class进行代替,这两个几乎没有差异,我么也可以为自己声明Stack类声明copy构造函数和assignment运算符,可以这样写:
template <typename T>
class Stack{
...
Stack(Stack<T> const&);
Stack<T>& operator=(Stack<T> const&);
...
};
然而如果只是需要class名称而不是class类型时,只需写Stack即可,构造函数和析构函数的声明属于这种情况。
**
使用class template
**
#include <iostream>
#include <string>
#include <cstdlib>
#include "stack.hpp"
int main(){
try{
Stack<int> intStack;
Stack<string> stringStack;
intStack.push(7);
std::cout<<intStack.pop()<<std::endl;
stringStack.push("hello");
std::cout<<stringStack.top()<<std::endl;
stringStack.pop();
stringStack.pop();
}catch(std::exception const& ex){
std::cerr<<"Exception:"<<ex.what()<<std::endl;
return EXIT_FAILURE;
}
}
需要注意的是,惟有被调用到的成员函数,才会被实例化。对于class template而言,只有当某个成员函数被调用时候,才回进行实例化。无疑这样做可以节省时间和空间。另一个好处是,你甚至可以实例化一个class template,而具体实现类型并不需要完整支持内部的部分不会被调用到的函数,举例如下:考虑么讴歌class,其某些成员函数使用operator<对内部元素进行排序;只要避免调用这些函数,就可以以一个不支持operator<的类型来实例化这个class template。
运用typedef,你可以更方便地使用class templates:
typedef Stack<int> IntStack;
void foo(IntStack const& s){
IntStack isStack[10];
...
}
Stack<float*> floatPtrStack;
Stack<Stack<int> > intStackStack;//Stack of Stack
注意,你必须在相邻的两个右角括号之间插入一些空白符号,否则就等于使用了operator>>,就会导致语法错误。
**
class Template的特化
**
针对某些特殊的template arguments,对一个class template进行特化,class template特化与fucntion template的重载类似,使你得以针对某些特定类型进行程序代码优化,或者修正某个特定类型在class template实例中的错误行为。如果你对一个class template进行特化,就必须特化所有成员函数。
特化必须以template<>开头声明此一class,后面耕者你希望的特化结果,特化类型将作为template arguments并在class名称之后直接写明:
template <>
class Stack<std::string>{
...
};
//特化函数而言,每个T出现处都应该更换为特化类型
void Stack<std::string>::push(std::string const& elem){
elems.push_back(elem);
}
下面给出一个针对std::string类型特化Stack<>的完整示例:
#include <deque>
#include <string>
#include <stdexcept>
#include "stack.hpp"
template<>
class Stack<std::string>{
private:
std::deque<std::string> elems;
public:
void push(std::string const&);
void pop();
std::string top() const;
bool empty() const{
return elems.empty();
}
};
void Stack<std::string>::push(std::string const& elem){
elem.push_back(elem);
}
void Stack<std::string>::pop(){
if(elems.empty()){
throw std::out_of_range("Stack<std::string>::pop():empty stack");
}
elems.pop_back();
}
std::string Stack<std::string>::top() const{
if(elems.empty()){
throw std::out_of_range("Stack<std::string>::top(): empty stack");
}
return elems.back();
}
该例中,我们在stack类中改用deque来管理替代元素,这样做并没有特别的好处,但它示范了一个特化实作码可以和其primary template有一定程度的差异,哈哈,这是不是类似于function template中函数重载呀!
**
偏特化(Partial Specialization)
**
class template不仅可以被全特化,更重要的是其可以被偏特化,强大吧!这时的我们可以在特定情况下使用特殊实作码,但仍然留给使用者选择template parameter的能力!例如针对如下class template:
template <typename T1,typename T2>
class MyClass{
...
}
以下书中形式的偏特化都是合理的:
template <typename T>
class MyClass<T,T>{
...
};
template <typename T>
class MyClass<T,int>{
...
};
template <typename T1,typename T2>
class MyClass<T1*,T2*>{
...
};
MyClass<int,float> mif;//使用Myclass<T1,T2>
Myclass<float,float> mff;//使用Myclass<T,T>
MyClass<float,int> mfi;//使用MyClass<T,int>
MyClass<int*,float*> mp;//使用<T1*,T2*>
MyClass<int,int> m;//使用???,匹配了两种
MyClass<int*,int*> m;//使用???,匹配了两种
为了解决上面的歧义性,可以针对相同类型的指针,再提供一种偏特化版本:
template <typename T>
class MyClass<T*,T*>{
...
};
**
预设模板自变量
**
我们可以针对class template定义其template parameters的默认值,称为default template arguments(预设模板自变量)
#include <vector>
#include <stdexcept>
template <typename T,typename CONT=std::vector<T> >
class Stack{
private:
CONT elems;
public:
void push(T const&);
void pop();
T top() const;
bool empty() const{
return elems.empty();
}
}
//因为template现在有两个参数,因此每一个成员函数的定义式中都必须包含这两个参数
template <typename T,typename CONT>
void Stack<T,CONT>::pop(){
if(elems.empty()){
throw std::out_of_range("Stack<>::pop(): empty stack");
}
elems.pop_back();
}
template <typename T,typename CONT>
T stack<T,CONT>::top() const{
if(elems.empty()){
throw std::out_of_range("Stack<>::top(): empty stack");
}
return elems.back();
}
template <typename T,typename CONT>
void Stack<T,CONT>::push(T const& elem){
elems.push_back(elem);
}
调用
#include <iostream>
#include <deque>
#include <cstdlib>
#include "stack.hpp"
int main(){
try{
Stack<int> intStack;
Stack<double,std::deque<double> >dblStack;
intStack.push(7);
std::cout<<intStack.top()<<std::endl;
intStack.pop();
dblStack.push(42.42);
std::cout<<dblStack.top()<<std::endl;
dblStack.pop();
dblStack.pop();
}catch(std::exception const& ex){
std::cerr<<"Exception:"<<ex.what()<<std::endl;
return EXIT_FAILURE;
}
}
注意上述代码中的intStack和dblStack的声明方式。