由于C++函数重载和模板函数是C++对C语言的扩展部分,属于学到的新知识,在这里进行下小结。

函数重载

函数重载比较简单,主要就是掌握函数特征标(function signature)–函数的参数列表
是特征标,而不是函数类型使得函数进行重载的。

C++编译器也是通过名称修饰(name decoration)来跟踪每一个重载函数的。

名称修饰或者名称矫正, 是C++编译器根据函数原型中指定的形参类型对每一个函数名进行加密。

C++标准允许每个编译器设计人员以他们认为合适的方式实现名称修饰,因此由不同编译器创建的二进制模块(对象代码文件)很可无法正确的链接。也就是说,两个编译器将为同一个函数生成不同的修饰名称。名称的不同将使连接器无法将一个编译器生成的函数调用与另一个编译器生成的函数定义相匹配。在链接编译模块时,请确保所有对象文件或者库都是用同一个编译器生成的。如果有源代码,通常可以用自己的编译器重新编译源代码来相处链接错误。

函数模板

函数模板就是使用泛型来定义函数,通过类型作为参数传给函数模板,编译器随后会生成相应类型版本的函数定义,供其他函数调用。
函数模板并不创建任何函数,而是告诉编译器如何定义函数。编译器检查所使用的参数类型,并生成相应的函数。最终的代码不包含任何模板,只包含了为程序生成的实际函数。

下面就来整理下函数模板的具体化。

模板函数具体化

隐式实例化、显式实例化和显式具体化统称为具体化(specialization), 他们表示的都是使用的具体类型的函数定义,而不是通用描述

隐式实例化

就是编译器根据提供的函数参数类型自动根据函数模板生成某种类型的函数定义,便是隐式实例化,不做特殊处理的模板函数的应用应该都是隐式实例化。

显式实例化

是直接命令编译器创建某一类型的函数实例,其语法是:生命所需的种类–用<>符号指示类型,并在声明前加上关键字template
显式实例化使用时相当于一个声明,可以在函数中使用,例如:

1
template void swap <char>(char &, char &); // 显式实例化

上面这句就相当于告诉编译器:“给我用swap()模板生成一个char型的函数定义”
其他没有显式实例化声明的函数就是通过隐式实例化根据实际调用时候的参数类型生成相应版本函数定义。

另外,可以直接在调用函数的时候进行显式实例化,引导编译器实例化出本人希望的函数定义,例如,

1
cout << Add<double>(x, m) << endl; // 强制为double类型实例化


显式具体化

我个人喜欢把具体化翻译成特殊化,其实就是单独再定义一个新的模板,模板函数的名称和之前的通用模板是相同的,但是内容不同,是个特殊的函数模板。原型语法如下:

1
2
template <> void swap<char>(char &, char &);
template <> void swap(char &, char &);

上面这句就相当于告诉编译器:“不要用swap的通用模板来生成函数定义,使用专门为char类型的模板生成函数定义”



关于上面这些概念《C++ Primer Plus》上用了一段代码进行了总结,我觉得挺好的,就贴上来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
template <class T>
void swap (T &, T &); // 模板原型
template <> void swap<job>(job &, job &); // job结构的显式具体化
int main(void)
{
template void swap<char> (char &, char &); // 针对char类型的显式实例化
short a, b;
...
swap(a, b); // 编译器自己决定的将模板swap隐式实例化
job n, m;
...
swap(m, n); // 使用显式具体化的模板进行实例化
char g, h;
...
swap(g, h); // 对swap模板进行显式实例化
...
}

重载解析寻找最匹配函数优先级

如果只存在一个这样的函数,选择之。
如果存在多个的话,且都为模板函数,选择最具体的模板函数

Comments