类模板 函数模板 全特化 偏特化 重载 匹配优先级

模板简介

模板提供了代码泛化的功能, 同一段代码可应用在不同类型.

模板不是魔法, 不是使用了一种神奇的方法使得代码可以适应不同类型的变量.
实际上, 在编译阶段, 编译器会实例化生成多份不同模板参数的代码, 等价于程序员写了多份不同类型参数的代码, 只不过是编译器替程序员完成了这部分工作罢了.

为什么模板要有特化

模板的代码不符合某些类型的要求, 比如写一个把原迭代器的内容拷贝到目标迭代器的copy函数, 对于类对象需要一个个拷贝,但是对于int, char等指针类型只需要一个memcpy就可以了, 此时就需要一个针对指针类型的特殊版本.

此外, C++标准库中的vector<bool>也是vector<T>的特化版本, 有兴趣的可以自己搜一下资料.

模板的特化分为偏特化和全特化, 下面分别介绍

偏特化

偏特化只用于类模板, 因为c++委员会说你不能("you can't partially specialize them -- pretty much just because the language says you can't.") 见Why Not Specialize Function Templates?
也有一些方法可以另类实现函数模板的偏特化, 这篇文章里有提到C++函数模板的偏特化 - 知乎, 以后再详细看看.

STL源码剖析的第87页中提到了偏特化的几个定义:

  • "所谓partial specialization的意思是提供另一份template定义式, 而其本身仍为templatized "
  • "针对(任何)template参数更进一步的条件限制所设计出来的特化版本"

根据这两条定义, 我们可以提取出偏特化的两个性质:

  1. 对已有模板的模板参数进行限制
  2. 本身还是一个模板

偏特化在类名后加上<...>进行模板参数指定
典型的偏特化有两种形式, 第一种是对多个模板参数中的某些(非全部)进行类型指定, 如:

template<typename A, typename B>
class C{...};//模板, 接受任意类型

template<typename A>
class C<A, int>{};//偏特化模板, 第一个参数为任意类型, 第二个参数只能为int

第二种偏特化形式, 不是很好理解, 是对指针, const等进行指定

template<typename T>
class C{...};//模板, 接受T为容易类型

template<typename T>
class C<T*>{};//偏特化版本, 只接受T为指针的情况

template<typename T>
class C<const T>{};//偏特化版本, 只接受T为const的情

不过可以看出它还是符合上面说的偏特化的两条性质的.

第二种偏特化的情况易和下面这种函数模板混淆

template<class T> 
void f( T* );//函数模板, 

没仔细看到话还以为这也是一种偏特化, 实际上这就是一个普通的函数模板(因为函数名后无<>进行模板参数指定), 并且函数模板也没有偏特化

全特化

全特化在类名后加上<...>进行模版参数指定
全特化比偏特化理解起来简单, 全特化需要指定所有模板参数, 全特化本身已经不是一个模板了, 如:

template<typename A, typename B>
class C{...};//模板, 接受任意类型

template<>
class C<double, int>{};//全特化, 第一个参数为double, 第二个参数只能为int

函数模板的重载

函数模板之间也可以重载, 如:

template<class T> 
void f( T );//接受任意类型

template<class T> 
void f( T* );//接受指针类型

函数模板和普通函数的匹配优先级

此部分主要参考 Why Not Specialize Function Templates?

牢记一点: 特化的模板不参与重载!!

对于模板、模板的全特化和模板的偏特化, 以及同名普通函数都存在的情况下,编译器在编译阶段进行匹配时,只匹配普通函数和模板, 匹配顺序如下:
1. 查找普通函数中有没有匹配的,如果有就选它
2. 查找模板中有没有匹配的, 并选则最匹配的版本, 然后进行下面两步

注意, 上面规则没提到特化版本,
如果编译器匹配到了规则2, 然后才进行特化版本的匹配

  1. 查找全特化版本中有没有匹配的
  2. 查找偏特化版本中有没有匹配的

特化版本不参与重载的原因是C++委员会认为因为写了一个特化版本而使得原先的重载函数模板匹配结果发生改变是不能接受的.

由于函数模板特化后的匹配行为很容易让人迷惑, 实际中不推荐使用函数模板的特化, 而选择使用普通函数!

下面的这个例子可以加深对特化模板匹配规则的理解,

using std::cout, std::endl;
template <class T>
void f(T x); // a

template <>
void f<>(int *x);//b

template<class T>
void f(T *x)://c

int main()
{
    int *p;
    f(p); //匹配的是c

    return 0;
}
  1. 首先编译器在匹配f(p)时, 发现有两个同名的模板a, c,
  2. 然后编译器发现c更加接近f(p), 所以就匹配c, 根本没考虑特化版本b

下面的代码展示了, 存在一个和全特化版本相同的普通函数, 优先匹配普通函数:

#include<algorithm>
#include <iostream>

using std::cout;
/// 函数模板偏特化demo
template <typename A, typename B>
void f(A a, B b) {
    std::cout << "Template version." << std::endl;
}

void f(int a, int b) {
    std::cout << " Normal version." << std::endl;
}

template <>
void f<int, int>(int a, int b) {
    std::cout << "Partial version." << std::endl;
}
int main()
{

    // 测试代码
    int a = 10;
    double b = 12;
    f(a, b);// template version
    f(a, a);// Normal version
    return 0;
}

最后, 再来查看一下普通函数和模板全特化函数的编译出的符号有什么区别, 下图是g++ 8.1.0的结果
可以看到模板函数的符号会另外添加一些额外字符, 可以和普通函数区分, 因此编译不会出现multi definition的错误.


image.png

总结:

  1. 类模板可以全特化, 偏特化
  2. 函数模板只能全特化, 但不推荐使用函数模板的全特化, 因为特化的模板在重载时的匹配行为很容易让人迷惑, 建议用普通函数代替
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容

  • 本文的标题改为陈述句可能更合适:为什么不该特化函数模板。 重载 v.s. 特化 为了更好的理解,我们先快速地回顾一...
    suesai阅读 2,200评论 0 0
  • 一、为什么要有函数模板 在泛型编程出现前,我们要实现一个swap函数得这样写: 但这个函数只支持int型的变量交换...
    许了阅读 8,555评论 1 5
  • 1 函数模板的定义 template<类型参数表> 返回值 函数名(数据参数表) { 函数模板定义体; } 例...
    Mr小熊_1da7阅读 143评论 0 0
  • 作者: 雪山肥鱼 时间:20211218 16:45 目的: 函数模板 函数模板的实例化 函数模板可以兼容多个类型...
    404Not_Found阅读 120评论 0 0
  • 重载模板以适用不同的情况 下面用于交换两个对象的函数模板exchange可以处理简单类型,但如果T是下面的类,就没...
    奇点创客阅读 243评论 0 0