//函數模板的定義格式
template<class 形參名,class 形參名...>
返回值型別 函數名(參數列){
函數體;
}
template <class T>
T Add(T x1, T x2){
return x1+x2;
}
int main(){
cout<<Add(2,3)<<endl;//5
cout<<Add(3.2,1.4)<<endl;//4.6
return 0;
}
函數模板範例化
不能直接使用函數模板實現具體操作,必須對模板進行範例化,即將模板引數範例化,就是用具體的型別引數去替換函數模板中的模板引數,生成一個確定的具體型別的真正函數,才能實現運算操作
範例化方式
隱式範例化
上述程式碼中呼叫Add(2,3)
的過程
編譯器根據傳入的實參2和3推斷出模型形參型別是int
會將函數模板範例化出一個int型別的函數
int Add(int x1,int x2){
return x1+x2;
}
編譯器生成具體型別函數的過程稱為範例化,生成的函數稱為模板函數
生成int型別的函數後,再將2和3傳入進行計算
當呼叫Add(3.2,1.4)
又會生成一個float的函數
每次呼叫都會根據不同的型別範例化出不同型別的函數,所以最終可執行程式的大小和過載方式相比並不會減少,只是提高了程式設計師對程式碼的複用
問題:隱式範例化不能為同一個模板形參指定兩種不同的型別,例如Add(2,3.2)
,此時編譯器會報錯,因為編譯器不能推斷出T的型別
顯式範例化
cout<<Add<int>(2,3.2)<<endl;//5 cout << Add<int>(2, static_cast<int>(3.2)) << endl;最好的方式是進行一個顯式轉換
cout<<Add<float>(3,2)<<endl;//5
函數模板也可以進行過載
template <class T>
T Add(T x1, T x2){
return x1+x2;
}
template <class T>
T Add(T x1, T x2, T x3){
return x1 + x2 + x3;
}
int main(){
cout<<Add(2,3)<<endl;//5
cout<<Add(3.2,1.4)<<endl;//4.6
cout<<Add<int>(2,3.2)<<endl;//5 cout << Add<int>(2, static_cast<int>(3.2)) << endl;最好的方式是進行一個顯式轉換
cout<<Add<float>(3,2)<<endl;//5
cout<<Add(1,2,3)<<endl;//6
return 0;
}
//類別範本定義格式
template <class 形參名, class 形參名>
class 類名{
}
template <class T1, class T2>
class Add{
private:
T1 x1;
T2 x2;
public:
Add(T1 x1, T2 x2):x1(x1),x2(x2){}
T1 get(){
return x1 + x2;
}
};
int main(){
Add<double,int> a(1.2,2);
cout<<a.get()<<endl;//3.2
return 0;
}
類別範本範例化
Add<double,int> *p1 = new Add<double,int>(1.2,2);
但是需要注意兩邊的模板引數需要相同template <class T>
class A{
public:
T Add(T t1, T t2){
return t1 + t2;
}
};
int main(){
A<int> a;
cout<<a.Add(1.2,2)<<endl;
}
在函數模板中,不能進行自動轉換,這是因為函數模板中需要根據實參來推斷資料型別,而在類別範本中,因為已經顯式指定了int,所以會建立一個int的類,然後可以自動轉換
在類別範本外定義成員函數,格式
template <模板形參表>
函數返回型別 類名<模板形參名>::函數名(參數列){}
template <class T>
class A{
public:
T Add(T t1, T t2){
return t1 + t2;
}
T sub(T t1, T t2);
};
template<class T>
T A<T>::sub(T t1, T t2) {
}
非模板友元函數
#include <iostream>
using namespace std;
template <class T>
class A{
private:
T x1,x2;
static T x3;
public:
A(T x1,T x2):x1(x1),x2(x2){}
friend void func();
};
//這裡進行特化賦值
template<>
int A<int>::x3 = 10;
template<>
double A<double>::x3 = 100;
void func(){
cout<<A<int>::x3<<endl;//10
cout<<A<double>::x3<<endl;//100
A<char> a('A','a');
cout<<a.x1<<" "<<a.x2<<endl;//A a
}
int main(){
func();
return 0;
}
#include <iostream>
using namespace std;
template <class T>
class A{
private:
T x1,x2;
static T x3;
public:
A(T x1,T x2):x1(x1),x2(x2){}
friend void func(A<T> a1);
};
//這裡進行特化賦值
template<>
int A<int>::x3 = 10;
template<>
double A<double>::x3 = 100;
void func(A<int> b){
cout<<A<int>::x3<<endl;//10
// cout<<A<double>::x3<<endl;//100此時就不可以存取double型別了
A<char> a('A','a');//可以正常建立別的型別的物件,因為這個建立是在任何地方都可以
// cout<<a.x1<<" "<<a.x2<<endl;//A a 這裡也不可以存取了,因為不是char型別的友元函數
}
int main(){
func(A<int>(1,2));
return 0;
}
約束模板友元函數
#include <iostream>
using namespace std;
template <class T>
void func(T x1, T x2);
template <class T>
class A{
private:
T x1,x2;
public:
A(T x1, T x2):x1(x1),x2(x2){}
friend void func<T>(T x1,T x2);//這裡也可以寫成friend void func<>(T x1, T x2);因為可以推斷出T的型別來
};
template <class T>
void func(T x1,T x2){
A<T> a1(x1,x2);
cout<<a1.x1<<" "<<a1.x2<<endl;
cout<<sizeof(A<T>)<<endl;
}
int main(){
func(1,2);//1 2 8
func<double>(1,2);// 1 2 16
}
非約束模板友元函數
#include <iostream>
using namespace std;
template <class T>
void func(T x1, T x2);
template <class T>
class A{
private:
T x1,x2;
public:
A(T x1, T x2):x1(x1),x2(x2){}
template<class U,class V>
friend void func(U u, V v);
};
template <class U, class V>
void func(U u, V v){
cout<<u.x1<<endl;
cout<<v.x1<<endl;
}
int main(){
A<int> a1(1,2);
A<double> a2(2.3,4.5);
func(a1,a2);//1 2.3
}
類別範本特化
#include <iostream>
using namespace std;
template <class A, class B>
class C{
public:
C(){
cout<<"template<class A, class B> class C"<<endl;
}
};
//全特化
template <>
class C<int,double>{
public:
C(){
cout<<"template<> class C<int,double>"<<endl;
}
};
//偏特化
template <class A>
class C<A,int>{
public:
C(){
cout<<"template<class A> class C<A,int>"<<endl;
}
};
//偏特化不一定指的是特化部分引數,而是對模板型別的進一步限制
template <class A, class B>
class C<A*,B*>{
public:
C(){
cout<<"template<class A,class B> class C<A*,B*>"<<endl;
}
};
int main(){
C<int,int> c1;//template<class A> class C<A,int>
C<double,double> c2;//template<class A, class B> class C
C<int,double> c3;//template<> class C<int,double>
C<int*,double> c4;//template<class A, class B> class C
C<int*,double*> c5;//template<class A,class B> class C<A*,B*>
}
函數模板特化
#include <iostream>
using namespace std;
template <class T1,class T2>
int compare(const T1& v1, const T2& v2){
cout<<"template <class T>"<<endl;
return 0;
}
template <>
int compare<int,int>(const int& v1, const int& v2){
cout<<"template<>"<<endl;
return 0;
}
//不支援偏特化
//template <class T1>
//int compare<T1,int>(const T1& v1, const int& v2){
// cout<<"template<class T1>"<<endl;
// return 0;
//}
int main(){
compare(1,1);//template<>
compare(1.2,2.3);//template <class T>
}
//方式1
//將第二個引數為int的情況排除掉
//然後再寫一個專門的一個引數的模板,這樣可以實現函數模板的功能
#include <iostream>
#include <type_traits>
template <typename A, typename B>
typename std::enable_if<!std::is_same<B, int>::value>::type f(A a, B b) {
std::cout << "template <typename A, typename B>" << std::endl;
}
template <typename A>
void f(A a, int b) {
std::cout << "template <typename A>" << std::endl;
}
int main() {
f(10, 5); // template <typename A>
f(10, 5.5); // template <typename A, typename B>
return 0;
}
//方式2
//使用結構體進行封裝
#include <iostream>
using namespace std;
template<class A,class B>
struct C{
C(A a,B b){}
void operator()() {
std::cout << "template<class A,class B>" << std::endl;
}
};
template <typename A>
struct C<A,int>{
C(A a,int b){}
void operator()() {
std::cout << "template <typename A>" << std::endl;
}
};
int main(){
C<int,int>(10,5)();//template <typename A>
C<int,double>(10,5.5)();//template<class A,class B>
return 0;
}
作者:孫建釗
出處:http://www.cnblogs.com/sunjianzhao/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。