泛化:模板,可以随便指定类型。
特化: 对特殊的类型(类型模板参数)进行特殊的对待,给他开小灶,给它写适合它的专用代码。
一: 类模板特化
<1>类模板全特化
1>常规全特化
必须先有泛化版本,才能存在特化版本。只要涉及特化,一定先存在泛化。
特化版本代码编译器会优先选择。
TC<char, int> tcchar;
tcchar.functest(); //泛化版本
TC<int, int> tcdint;
tcdint.functest(); //int, int的特化版本
//这种特化版本可以任意多;我们目前是写了两个。
2>特化成员函数而不是模板
TC<double, double> tdbldbl; //泛化版本对象, 调用的是泛化版本的构造函数。
tdbldbl.functest(); //因为我们特化了double, double类型的functest函数,所以这里调用的是特化的functest()函数。
template<typename T, typename U>
struct TC //泛化的TC类模板
{
TC()
{
cout << "TC泛化版本构造函数" << endl;
}
void functest()
{
cout << "TC泛化版本" << endl;
}
};
//当T和U这两个类型模板参数都为int类型时,我们希望做一个特化版本
//全特化:就是所有类型模板参数(这里T和U),都得用具体的类型代表。
template<> //全特化: 所有类型模板参数都用具体类型代表,所以这里的template后边的<>里为空
struct TC<int, int> //上边的T绑定到这里的第一个int,上边的U绑定到这里的第二个int
{
TC()
{
cout << "TC<int, int>的特化版本构造函数" << endl;
}
//在这里可以对该特化版本做单独处理
void functest()
{
cout << "TC<int, int>的特化版本" << endl;
}
};
template<> //全特化: 所有类型模板参数都用具体类型代表,所以这里的template后边的<>里为空
struct TC<double, int> //上边的T绑定到这里的第一个double,上边的U绑定到这里的第二个int
{
//在这里可以对该特化版本做单独处理
void functest()
{
cout << "TC<double, int>的特化版本" << endl;
}
};
template<> //全特化
void TC<double, double>::functest()
{
cout << "double, double的functest()的特化版本" << endl;
}
int main()
{
TC<char, int> tcchar;
tcchar.functest(); //泛化版本
TC<int, int> tcdint;
tcdint.functest(); //int, int的特化版本
TC<double, double> tdbldbl; //泛化版本对象, 调用的是泛化版本的构造函数。
tdbldbl.functest(); //因为我们特化了double, double类型的functest函数,所以这里调用的是特化的functest()函数。
return 0;
}
<2>类模板偏特化 (局部特化)
偏特化从两方面说起:一个是从模板参数数量上,一个是从模板参数范围上
1>模板参数数量
TC<double, int, double> tcdi;
tcdi.functest(); //泛化版本
TC<int, int, double> teii;
teii.functest(); //偏特化int, U, double版本
//泛化版本
template <typename T, typename U, typename W> //带三个类型模板参数
class TCP
{
public:
TCP()
{
cout << "TCP泛化版本构造函数" << endl;
}
void functest()
{
cout << "TCP泛化版本" << endl;
}
};
//从参数数量上进行偏特化,我们现在绑2个类型模板参数。留1个模板类型参数。
template<typename U> //因为另外两个被我绑定到具体类型,所以这里只剩下一个U类型模板参数了。
struct TCP<int, U, double> //大家注意,这里可以跳着来
{
TCP()
{
cout << "TCP<int, U, double>偏特化版本构造函数" << endl;
}
void functest()
{
cout << "TCP<int, U, double>偏特化版本" << endl;
}
};
int main()
{
TCP<double, int, double> tcpdi; //TCP泛化版本构造函数
tcpdi.functest(); //TCP泛化版本
TCP<int, int, double> tcpii; //TCP<int, int, double>偏特化版本构造函数
tcpii.functest(); //TCP<int, int, double>偏特化版本
return 0;
}
2>模板参数范围上:int, const int(比int小),
原来T,现在T* (从任意类型T索晓伟指针类型T*)
原来是T,现在T&左值引用,或者现在是T&&(右值引用),都叫范围缩小。
TC td;
td.functest(); //泛化版本
TC<double*> tpd;
tpd.functest(); //T*特化版本
TC<const double*> tpd2;
tpd2.functest(); //T*特化版本
TC td3;
td3.functest(); //const T特化版本
TC<int&> tcyi;
tcyi.functest(); //T&左值引用特化版本
TC<int&&> tcyii;
tcyii.functest(); //T&&右值引用特化版本
局部特化,特化完了之后它本质上还是个模板。全特化,特化完了等于一个具体的类了。
//泛化版本
template <typename T>
struct TC
{
void functest()
{
cout << "泛化版本" << endl;
}
};
//模板参数范围上的特化版本
template <typename T>
struct TC<const T>
{
void functest()
{
cout << "const T特化版本" << endl;
}
};
template <typename T>
struct TC<T*> //T*的特化版本,告诉编译器,如果使用指针,就调用这个版本。
{
void functest()
{
cout << "T*特化版本" << endl;
}
};
template <typename T>
struct TC<T&> //左值引用的特化版本
{
void functest()
{
cout << "T&特化版本" << endl;
}
};
template <typename T>
struct TC<T&&> //右值引用的特化版本
{
void functest()
{
cout << "T&&特化版本" << endl;
}
};
int main()
{
TC<double> td;
td.functest(); //泛化版本
TC<double*> tpd;
tpd.functest(); //T*特化版本
TC<const double*> tpd2;
tpd2.functest(); //T*特化版本
TC<const double> td3;
td3.functest(); //const T特化版本
TC<int&> tcyi;
tcyi.functest(); //T&左值引用特化版本
TC<int&&> tcyii;
tcyii.functest(); //T&&右值引用特化版本
return 0;
}
二:函数模板特化
<1>函数模板全特化
const char* p = “I love China!”;
int i = 12;
tfunc(p, i); //T = const char * , tmprv = const char * & , U = int , tmprv2 = int&
int i = 12;
double db = 15.8f;
tfunc(i, db);
全特化函数模板实际上等价于实例化一个函数模板,并不是等价于一个函数重载
void tfunc<int, double>(int& tmprv, double& tmprv2){} //全特化,等价于实例化一个函数模板
void tfunc(int& tmprv, double& tmprv2){} //重载函数应该长这样
编译器选择最最合适的:普通优先,特化版本,泛化版本;(优先级顺序排列)
如果你传递个字符串给函数模板
函数模板的特化版本中,如果有数组类型模板参数, 指针类型模板参数。
编译器会认为数组类型模板参数比指针类型模板参数更合适。所以编译器会为你选择数组类型的模板参数的特化版本。
<2>函数模板偏特化
函数模板不能偏特化
//函数模板泛化版本
template<typename T, typename U>
void tfunc(T& tmprv, U& tmprv2)
{
cout << "tfunc泛化版本" << endl;
cout << tmprv << endl;
cout << tmprv2 << endl;
}
函数模板偏特化
//template<typename U>
//void tfunc<double, U>(double& tmprv, U& tmprv2) //“tfunc”: 非法使用显式模板参数
//{
// //......
//}
//全特化版本,T = int, U = double
template<>
void tfunc(int& tmprv, double& tmprv2)
{
cout << "-------------begin---------------" << endl;
cout << "tfunc全特化版本int, double" << endl;
cout << tmprv << endl;
cout << tmprv2 << endl;
cout << "-------------end---------------" << endl;
}
//重载函数
void tfunc(int& tmprv, double& tmprv2)
{
cout << "tfunc重载函数(int, double)" << endl;
}
int main()
{
const char* p = "I love China!";
int i1 = 12;
tfunc(p, i1); //T = const char * , tmprv = const char * & , U = int , tmprv2 = int&
int i2 = 12;
double db = 15.8f;
tfunc(i2, db);
return 0;
}
三: 模板特化版本放置位置建议
模板定义、实现都放在一个 .h中;
模板的特化版本和模板的泛化版本都应该放在同一个 .h 文件中。
.h 文件中前面放泛化版本,后面放特化版本。