[C++ Template]深入模板--特化与重载

本文深入探讨C++中的模板特化与重载,包括重载函数模板的签名、局部排序原则、显式特化,以及局部类模板特化的概念和用法。通过示例解释了模板重载可能导致的二义性问题,以及解决这些问题的策略,展示了如何利用模板特化优化代码,特别是针对指针类型的模板实例。
摘要由CSDN通过智能技术生成

目录

第12章 特化与重载

12.2 重载函数模板

12.2.1 签名

12.2.2 重载的函数模板的局部排序

12.2.3 正式的排序原则

12.2.4 模板和非模板

12.3 显式特化

12.3.1 全局的类模板特化

12.3.2 全局的函数模板特化

12.3.3 全局成员特化

12.4 局部的类模板特化


第12章 特化与重载

目前为止,我们已经知道了:C++模板如何使一个泛型定义扩展成一些相关的类家族或者函数家族。虽然这是一个功能很强大的机制,但该机制并非适合于所有的情况;在一些情况下,这种泛型操作就不是特定模板参数替换的最佳选择。

 

12.2 重载函数模板

两个同名的函数模板可以同时存在,还可以对它们进行实例化,使它们具有相同的参数类型。下面是另一个简单的例子:

template<typename T>
int f(T)
{
    return 1;
}
template<typename T>
int f(T*)
{
    return 2;
}

如果我们用int*来替换第1个模板的T,用int来替换第2个模板的T,那么将会获得两个具有相同参数类型(和返回类型)的同名函数。也就是说,不仅是同名模板可以同时存在,它们各自的实例化体也可以同时存在,即使这些实例化体具有相同的参数类型和返回类型。

 

12.2.1 签名

只要具有不同的签名,两个函数就可以在同一个程序中同时存在。我们对函数的签名定如下

1.非受限函数的名称(或者产生自函数模板的这类名称)。

2.函数名称所属的类作用域或者名字空间作用域;如果函数名称是具有内部链接的,还包括该名称声明所在的翻译单元。\

3.函数的const、volatile或者const volatile限定符(前提是它是一个具有这类限定符的成员函数)。

4.函数参数的类型(如果这个函数是产生自函数模板的,那么指的是模板参数被替换之前的类型)。

5.如果这个函数是产生自函数模板,那么包括它的返回类型。

6.如果这个函数是产生自函数模板,那么包括模板参数和模板实参。

这就意味着:从原则上讲,下面的模板和它们的实例化体可以在同个程序中同时存在:

template<typename T1, typename T2>
void f1(T1, T2);
template<typename T1, typename T2>
void f1(T2, T1);
template<typename T>
long f2(T);
template<typename T>
char f2(T);

然而,如果上面这些模板是在同一个作用域中进行声明的话,我们可能不能使用某些模板,因为实例化过程可能会导致重载二义性。例如:

template<typename T1, typename T2>
void f1(T1, T2)
{
    std::cout << "f1(T1, T2)\n";
}

template<typename T1, typename T2>
void f1(T2, T1)
{
    std::cout << "f1(T2, T1)\n";
}
// 到这里为止一切都是正确的

int main()
{
    f1<char, char>('a', 'b'); // 错误:二义性
}

在上面的代码中,虽然函数f1<T1 = char, T2 = char>(T1,T2)可以和函数f1<T1 = char, T2 =char>(T2, T1)同时存在,但是重载解析规则将不知道应该选择哪一个函数。

 

12.2.2 重载的函数模板的局部排序

template<typename T>
int f(T)
{
    return 1;
}

template<typename T>
int f(T*)
{
    return 2;
}

int main()
{
    std::cout << f(0) << std::endl;
    std::cout << f((int*)0) << std::endl;
}

让我们先考虑调用(f(0)):实参的类型是int,如果用int替换T,就能和第1个模板的参数匹配。然而,第2个模板的参数类型总是一个指针;因此,经过演绎之后,只有产生自第1个模板的实例才是该调用的候选函数。在这个调用中,重载解析并没有发挥作用。

第2个调用((f ( (int*) 0) )就显得比较有趣:对于这两个模板,实参演绎都可以获得成功,于是就获得两个函数,即f<int*>(int*)和f<int>(int*)。如果根据原来的重载解析观点,这两个函数和实参类型为 int*的调用的匹配程度是一样的,这也就意味着该调用是二义性的(见附录B)。然而,在这种情况下,还应该考虑重载解析的额外规则:选择“产生自更特殊的模板的函数”。因此(我们将在后面的小节看到),第2个模板被认为是更加特殊的模板,从而(再次)产生下面的输出结果:

1 
2


12.2.3 正式的排序原则

接下来,我们将给出一个精确的过程,它能够判断:在参与重载集的所有函数模板中,某个函数模板是否比另一个函数模板更加特殊。然而,我们应该知道这只是不完整的排序原则:就是说,两个模板也可能会被认为具有相同的特殊程度。如果重载解析必须在这两个特殊程度相同的模板中进行选择,那么将不能做出任何决定,也就是说程序包含了一个二义性错误。

假设我们要比较两个同名的函数模板ft1和ft2,对于给定的函数调用,它们看起来都是可行的。在我们下面的讨论中,对于没有被使用的缺省函数实参和省略号参数,我们将不考虑。接下来,通过如下替换模板参数,我们将为这两个模板虚构两份不同的实参类型(如果是转型函数模板,那么还包括返

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值