函數模板
函數模板的定義
template<class T> //模板參數列表 不能使用struct //自動識別參數類型
template<typename T> //自動識別參數類型
//typename是用來定義模板參數的關鍵字,也可以使用class
//template<int T> //編譯出錯
T Add(T left, T right) //函數模板
{
return left + right;
}
Add(1, 2); //模板的實例化
template<class T1, class T2> //多個類型參數
T2 Add(T1 left, T2 right)
{
return left + right;
}
Add(1, 2.0);
函數模板的原理
模板是一個藍圖,本身並不是函數,是編譯器用使用方式產生特定具體類型函數的模具,在調用時編譯器會根據調用方式生成函數代碼
模板匹配規則
- 一個非模板函數可以和同名的一個函數模板存在,而且該函數模板還可以被實例化爲這個非模板函數
- 對於非模板函數和同名函數模板,如果其他條件都相同,在調用時會優先調用非模板函數而不會從該模板產生一個實例,如果模板可以產生一個具有更好的匹配函數,那麼將選擇模板
- 模板函數不允許自動類型轉化,但普通函數可以自動類型轉化
T Add(T left, T right)
{
return left + right;
}
int Add(int left, int right) //參數類型符合時,會調用用戶自己實現的,且編譯器不會再生成
{
return left + right;
}
函數模板的實例化
多次調用,只生成一份代碼
//隱式實例化
Add(1,2); //對實參進行類型推演,生成處理具體類型的函數
//不會進行隱式類型轉化(除數組——>指針、函數名——>函數指針)
//顯示實例化
Add<int>(1,2); //確定的參數類型,會進行隱式類型轉化
//顯示實例化時,編譯器必須生成代碼
Add<>(1,2); //隱式實例化,但也需要生成代碼
類模板
類模板的定義
template<class T1, class T2, ..., class Tn>
class 類模板名
{
// 類內成員定義
};
類模板的實例化
類模板實例化與函數模板實例化不同,類 模板實例化需要在類模板名字後跟<>,然後將實例化的類型放在<>中即可,類模板名字不是真正的類,而實例化的結果纔是真正的類。 在這裏,我們實現一個簡單的棧,來展示一個類模板。
#define MAXSIZE 10
template<class T>
class STACK
{
public:
STACK()
{
_pStack = new T[10];
_size = 0;
}
void StackPush(T data)
{
if (_size == MAXSIZE)
{
std::cout << "full" << std::endl;
return;
}
_pStack[_size] = data;
++_size;
}
void StackPop()
{
if (_size == 0)
{
std::cout << "empty" << std::endl;
return;
}
--_size;;
}
T StackTop()
{
if (_size == 0)
{
std::cout << "empty" << std::endl;
return -1;
}
return _pStack[_size - 1];
}
~STACK()
{
delete[] _pStack;
_pStack = nullptr;
_size = 0;
}
private:
T* _pStack;
int _size;
};
STACK<int> s;
非類型模板
模板參數
類型形參:出現在模板參數列表中,跟在class或者typename之類的參數
非類型形參:用一個常量作爲類(函數)模板的一個參數
//非類型模板參數 N 常量
template<class T, size_t N>
//函數
//數組引用,類似於數組指針
void print(const T (&array)[N])
{
)
print(arr); //編譯器會把元素個數傳遞給N
//類
template<class T, size_t N>
class Array
{
T _array[N];
}
Array<int, 100> arr;
注意
- 浮點數、類對象以及字符串是不允許作爲非類型模板參數的
- 非類型模板參數必須在編譯期就能確認結果
模板的特化
在原模板類的基礎上,針對特殊類型所進行特殊化的實現方式
函數模板的特化
必須要先有一個基礎的函數模板,那種類型不能處理,特殊化處理該種類型
- 關鍵字template後面接一對空的尖括號<>
- 函數名後跟一對尖括號,尖括號中指定需要特化的類型
- 用需要特化的類型替換掉所有的T
/*
假設char*需要特殊處理,用char*替換掉所有的T
需要特化類型,將什麼類型跟在函數名之後
*/
Template<>
bool IsEqual<char*>(char*& left, char*& right)
{
if(strcmp(left, right) > 0)
return true;
return false;
}
//一般情況下如果函數模板遇到不能處理或者處理有誤的類型,爲了實現簡單通常都是將該函數直接給出(簡單)。
bool IsEqual(char* left, char* right)
{
if(strcmp(left, right) > 0)
return true;
return false;
}
//在模板中,const T pa,const實際是修飾的是pa
類模板的特化
全特化
全特化即是將模板參數類表中所有的參數都確定化
template<class T1, class T2>
class Data
{
public:
Data()
{
cout << "Data<T1, T2>" << endl;
}
private:
T1 _d1;
T2 _d2;
};
//類模板的全特化
template<>
class Data<int, char>
{
public:
Data()
{
cout << "Data<int, char>" << endl;
}
private:
int _d1;
char_d2;
};
偏特化
任何針對模版參數進一步進行條件限制設計的特化版
部分特化
將模板參數類表中的一部分參數特化
//將第二個參數特化
int template <class T1>
class Data<T1, int>
{
public:
Data()
{
cout<<"Data<T1, int>" <<endl;
}
private:
T1 _d1;
int _d2;
};
特化類型
//兩個參數偏特化爲指針類型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
Data()
{
cout<<"Data<T1*, T2*>" <<endl;
}
private:
T1 _d1;
T2 _d2;
};
//兩個參數偏特化爲引用類型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
Data(const T1& d1, const T2& d2)
: _d1(d1) , _d2(d2)
{
cout<<"Data<T1&, T2&>" <<endl;
}
private:
const T1 & _d1;
const T2 & _d2;
};