持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情
类模板特化(specializations)
允许对一个类模板的某些模板参数类型做特化,特化的作用或好处在于,对于某种特殊的类型,可能可以做些特别的优化或提供不同的实现,避免在实例化类的时候引起一些可能产生的诡异行为。
- 特化一个类模板的时候也意味着需要特化其所有参数化的成员
- 如果要特化一个类,那么做法是,声明一个带
template<>的类,即空参数列表 - 在类名称后面紧跟的尖括号中显式指明类型
可以实现所有成员函数以及存放成员的容器,也可以添加一个新函数作为特化类模板的成员函数
接下来通过一个例子来帮助理解模板特化(specializations),下面定义类模板,Storage 用于将数据进行持有。
template<typename T>
class Storage
{
private:
T m_val{};
public:
Storage(T val) : m_val(val) {}
void print()
{
std::cout << m_val << std::endl;
}
};
上面代码看起来比较简单易懂,定义类型参数 T 这样 Storage 就是用于持有不同类型的数据。然后在类中定义一个print 方法用于查看持有数据。
如果想要持有类型为 double 数据时,输出 double 类型数据以科学计数法来表示输出数据。
template<>
void Storage<double>::print()
{
std::cout << std::scientific << m_val << std::endl;
}
这样输出时,就可以针对 double 类型输出科学计数法形式的输出了,也就是模板特化的用途,可以重新定义一下 print 方法,这个函数上面添加 template<> 然后 void Storage<double>::print() 也就是将 print 是当类型为 double 时候才会被调用。
Storage<char*> storage(s.data());
std::string s;
std::cout << "Enter your name: ";
std::cin >> s;
//std::cout << s.data() << std::endl;
Storage<char*> storage(s.data());
storage.print();
s.clear();
storage.print();
s.data()返回一个char*也就是返回字符串数组,char*为起始位置
观测两次storage.print() 输出的结果,分别是 s 中保存输入内容,而第二次输出则为空,这是因为当 s.clear() 时候将字符串清空了,所以在此输出就发现char* 指向内存地址存放数据已经被清空了。这不是我们想要看到的,我们希望 Storage 是可以持有数据的,所以进行如下修改,也就是当 T 指定为 char* 时候,做一些特殊处理,读取char 数据将其保存在 m_val ,首先保留内存空间用于保存 value
template<>
Storage<char*>::Storage(char* value)
{
if (!value)
return;
int length{ 0 };
while (value[length] != '\0')
++length;
++length;
m_val = new char[length];
for (int count = 0; count < length; ++count)
m_val[count] = value[count];
}
对于 Storage<char*> 进行特化,
偏特化(Partial specialization)
类模板也可以被偏特化,比如主模板如果定义
template <typename T1, typename T2> class MyCls{}
可能产生以下几种对于主模板的偏特化:
- 将模板参数偏特化为同样类型
template<typename T> class MyCls<T,T> {}
- 将第二个模板参数偏特化为
int类型,不再是泛型的T
template<typename T> class MyCls<T, int> {}
- 将两个类型偏特化为指针
template<typename T1, typename T2> class MyCls<T1*,T2*>{}
以上都是上面模板的特化,
- 如果不止一个偏特化同等程度地能够匹配某个调用,那么该调用具有二义性,编译器不会通过编译
MyCls<int,int> obj
MyCls<int*,int*> obj
默认模板实参
- 类似函数的默认参数,对于类模板而言也可以定义其模板参数的默认值,这些值就叫做默认模板实参
小结
- 模板类的性质是,有一个或多个类型未被指定
- 要使用一个模板类,就传入具体的类别作为
- 类模板可以用特定的类型特化(specialization)