函数模板

隐式实例化与显式实例化

常规的模板函数声明如下:

1
2
template <typename T>
T add(T arg1, T arg2);

模板实际上并不是一个可以直接调用的函数,只有首次使用函数时,编译器才根据参数类型生成一个对应版本的函数实例,然后调用该函数,这个过程称为隐式实例化。C++ 11新增显式实例化:

1
template int add(int, int);

该语句提示编译器强制生成一个int版本的模板函数实例,而不管接下来有没有用到该版本的函数。显式实例化可以用于模板函数的声明与定义分离,例如在头文件中声明函数模板,在源文件中定义函数体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//头文件 声明
template<typename T>
void myswap(T &arg1, T &arg2);

//源文件 定义
template<typename T>
void myswap(T &arg1, T &arg2)
{
arg1=arg1^arg2;
arg2=arg1^arg2;
arg1=arg1^arg2;
}
//不包含此显式实例化将产生link_error
template void myswap(int,int);

//main.cpp
#include "myswap.h"
int main()
{
int a,b;
a=1,b=2;
myswap(a,b)
return 0;
}

在模板实现文件中显式具体化后强制生成了对应函数实例,main中调用myswap函数时,编译器虽然无法访问函数定义生成main自己的函数实例,但在链接阶段找到了myswap.obj中签名相符的函数实例,因此链接通过。显式实例化避免了重复实例化,缺点是必须在模板实现文件中实例化所有需要的版本,适用于重载需求较少的模板。

显式实例化与具体化

模板只能解决“普遍情况”下的问题,对于特殊参数类型,通用模板就无法工作。为了解决类似的“具体”问题,需要为对应的参数类型实现“具体化”的模板定义:

1
2
3
4
template<> char* add(char* pc1, char* pc2)
{
//...
}

显而易见,具体化的模板优先级高于普通模板。如果在同一编译单元同时存在签名相同的显式实例化与具体化,且显式实例化在具体化之前,则会引发重复定义错误,原因是编译器先显式实例化生成了函数实例,但接下来的语句出现了优先级更高的具体化模板,编译器尝试以具体化模板生成函数实例,但已存在相同签名的函数实例,因此引发重复定义错误。反之编译器先以具体化模板为原型生成函数实例,则会跳过优先级较低的显式实例化,编译通过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//通用模板
template<typename T>
T add(T arg1, T arg2);
//a.显式实例化
template int add(int, int);
//b.相同签名的具体化模板
template<> int add(int, int);
//交换ab的次序则编译通过
int main()
{
int a=1,b=2;
add(a,b); //使用该签名的函数引发重复定义
}

//模板定义
/*.........

模板调用优先级

如果同时存在参数类型匹配的非模板函数、普通模板、具体化模板,则非模板 >具体化模板 >通用模板,其中具体化模板参数越具体,其优先级越高。调用时也可以强制使用模板函数,此时只考虑模板的优先级。

1
2
3
4
5
6
7
int main()
{
int a=1,b=2;
add(a,b); //优先调用非模板add(int,int)
add<>(a,b); //强制使用模板,类型自动推断
add<int>(a,b); //使用模板,显式指定模板参数类型为int
}

类模板

偏特化

类模板与函数模板使用方法类似,不过类模板多一个偏特化(或者叫部分具体化)语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
//通用模板
template<typename T>
class MyClass
{
//...
}

//偏特化模板,特化为指针
template<typename T>
class MyClass<T*>
{
//...
}

偏特化可以看作是对模板的进一步具体,介于完全具体与完全泛化之间。函数模板没有偏特化语法,因为可以用函数模板重载来实现一样的效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template<typename T>
void MyFunc(T);

//非法使用显式模板参数
template<typename T>
void MyFunc<T*>(T*);

//OK,这是另一种具体化语法,可省略<char*>
template<>
void MyFunc<char*>(char*);

//OK,构成模板重载
template<typename T>
void MyFunc(T*);

//不推荐。编译器将它看作显式实例化,忽略<typename T>以及函数定义。
template<typename T>
void MyFunc(char*);

以上代码测试结果均来自VS2013,具体情形以编译器实现为准

 评论




载入天数...载入时分秒...  |  总访问量为
Powered by Github and MarkdownPad 2

--------------------------- 别拉了,我是有底线的>_< ---------------------------