C++模版 - 偏特化与全特化

模版

定义

模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了真正的代码可重用性。 模版可以分为两类,一个是函数模版,另外一个是类模版。

C++的模板机制被证明是图灵完备的,即可以通过模板元编程(template meta programming)的方式在编译期做任何计算。

实例化

模板定义本身不参与编译,而是编译器根据模板的用户使用模板时提供的类型参数生成代码,再进行编译。 用户提供不同的类型参数,就会实例化出不同的代码。

类模版

类模板描述了一组相关的类或数据类型,它们只能通过类型来区分:整数值、指向(或引用)具有全局链接的变量的指针、其他的组合。 类模板尤其适用于描述通用但类型安全的数据结构。

1
2
3
4
5
6
7
8
9
10
11
template <类型形式参数>
class 类名
{
//类声明体;
};

template <类型形式参数>
返回类型 类名 <类型> :: 成员函数名(形式参数)
{
//成员函数定义体;
}

全特化

所谓模板全特化限定死模板实现的具体类型;

1
2
3
4
5
template <>
class A<int, double>{
int data1;
double data2;
};

偏特化

偏特化是指提供另一份template定义式,而其本身仍为templatized,这是针对于template参数更进一步的条件限制所设计出来的一个特化版本。 也就是如果这个模板有多个类型,那么只限定其中的一部分;

1
2
3
4
template <class T2>
class A<int, T2>{
...
};

函数模版

1
2
3
4
5
template <类型形式参数>      //类型形式参数即此格式:<typename  形式参数>  或 <class 形式参数>
返回类型 函数名 (形式参数)
{
//函数定义体;
}

全特化

函数模板全特化和类模板全特化本质是一样的,是对模板参数的特殊化处理。

1
2
3
4
template <>
int max(const int lhs, const int rhs){
return lhs > rhs ? lhs : rhs;
}

特化的歧义

1
2
3
4
5
template <class T>
void f(){ T d; }

template <>
void f(){ int d; }

此时编译器不知道f()是从f\()特化来的,编译时会有错误:

1
error: no function template matches function template specialization 'f'

这时我们便需要显式指定”模板实参”:

1
2
3
4
5
template <class T>
void f(){ T d; }

template <>
void f<int>(){ int d; }

偏特化

函数模版没有偏特化。

例如下面代码会编译出错:

1
2
3
4
5
template <class T1, class T2>
void f(){}

template <class T2>
void f<int, T2>(){}

但函数允许重载,声明另一个函数模板即可替代偏特化的需要:

1
2
template <class T2>
void f(){} // 注意:这里没有"模板实参"

多数情况下函数模板重载就可以完成函数偏特化的需要,一个例外便是std命名空间。 std是一个特殊的命名空间,用户可以特化其中的模板,但不允许添加模板(其实任何内容都是禁止添加的)。 因此在std中添加重载函数是不允许的,在Effective C++: Item 25中给出了一个更详细的案例。

总结

  1. 函数模板只有全特化,没有偏特化;
  2. 模板、模板的特化和模板的偏特化都存在的情况下,编译器在编译阶段进行匹配,优先特殊的;
  3. 模板函数不能是虚函数;因为每个包含虚函数的类具有一个virtual table,包含该类的所有虚函数的地址,因此vtable的大小是确定的。模板只有被使用时才会被实例化,将其声明为虚函数会使vtable的大小不确定。所以,成员函数模板不能为虚函数。

本文作者:jujimeizuo
本文地址https://blog.jujimeizuo.cn/2023/01/11/c-template-specialization/
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0 协议。转载请注明出处!