C++ Primer Plus 学习之函数模板

关键字decltype(C++11)
后置返回类型(C++11)


函数模板的声明

有时编写的模板函数很可能无法处理某些类型,这时候我们可以为特定类型提供具体化的模板定义。

隐式具体化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <typename T>//typename 也可以用class替换
void Swap(T &a,T &b)
{
T tmp=a;
a=b;
b=tmp;
}
int main()
{
int a=3,b=4;
Swap(a,b);
char x='a', y='b';
Swap(x,y);
cout<<x<<' '<<y<<endl;
cout<<a<<' '<<b<<endl;
}

显示具体化

可以理解为显示的定义一种模板函数为了特殊需要,注意必须有函数定义。(函数体)

  • 对于给定的函数名,可以有非模板函数,模板函数和显示具体化模板函数以及它们的重载版本。
  • 显示具体化的原型和定义应以template<>打头,并通过名称来指出类型。
  • 非模板版本优先于显示具体化,显示具体化优先于模板版本(非模板版本>显示具体化>模板版本)。

下面是用于交换job结构的非模板函数,模板函数和具体化的原型。

1
2
3
4
5
6
7
8
9
10
11
12
13
struct job
{
char name[40];
double salary;
int floor;
};
//非模板函数
void Swap(job &,job &);
//模板函数
template <typename T>
void Swap(T &,T &);
//显示具体化
template<> void Swap<job>(job &,job &);

显示实例化
可以理解为显示的定义一种模板函数为了特殊需要,没有函数定义。

显示实例化和显示具体化的区别(P288)

  • 在代码中包含函数模板并不会生成函数定义,它只是一个用于生成函数定义的方案。
  • 在调用模板函数时,编译器自动生成函数定义,得到模板实例 – 隐式实例化。
  • 显式实例化 – 前加template关键字 (不加尖括号),而显示具体化需要加。

    1
    2
    3
    4
    5
    6
    7
    // 显式实例化
    template void swap_diy<char>(char&, char&); // 只需要声明即可
    // 显式具体化
    template <> void swap_diy<char>(char&, char&)
    {
    // 特定语句
    }
  • 显示实例化没有函数体,显示具体化有。

  • 显示实例化命令编译器使用模板生成一个使用特定类型的函数定义 – 即生成模板实例;而显示具体化表示不要用模板来生成函数定义,而应该使用专门为特定类型显示定义的函数定义。
  • 不能在同一个文件中同时使用同一种类型的显式具体化和显式实例化。
  • 也可在函数调用时显式指定类型。
    1
    swap_diy<double>(a, b);

重载解析

步骤:

  • 创建候选函数列表。其中包含与被调用函数的名称相同的函数和模板函数。
  • 使用候选函数列表创建可行函数列表。这些都是参数数目正确的函数,为此有一个隐式转换序列,其中包括实参类型与相应的形参类型完全匹配的情况。
  • 确定是否有最佳的可行函数。(优先级从高到低)
    • 完全匹配,但常规函数优先于模板。
    • 提升转换(如,char和shorts自动转换为int,float自动转换为double)。
    • 标准转换(例如,int转换为char,long转换为double)。
    • 用户定义的转换,如类声明中定义的转换。

如果出现多个完全匹配的函数,编译器将会生成一条错误消息-“二义性”。但有时候,即使两个函数都完全匹配,仍可完成重载解析。首先指向非const数据的指针和引用优先于const指针和引用参数匹配。(注意只适用于指针和引用)。
另一个完全匹配优于另一个的情况是,其中一个是非模板函数,而另一个不是。这种情况下,非模板函数将优先于模板函数(包括显示具体化)。
如果两个完全匹配的函数都是模板函数,则较具体的模板函数优先。这意味着显示具体化将优于使用模板隐式生成的具体化。