深入分析C++模板特化与偏特化

1.模板特化

1.1概述

模板特化(template specialization)不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板的特化。模板特化有时也称之为模板的具体化,分别有函数模板特化和类模板特化。

1.2函数模板特化

函数模板特化是在一个统一的函数模板不能在所有类型实例下正常工作时,需要定义类型参数在实例化为特定类型时函数模板的特定实现版本。查看如下例子。

#include <iostream>
using namespace std;

template<typename T> T Max(T t1,T t2)
{
return (t1>t2)?t1:t2;
}

typedef const char* CCP;
template<> CCP Max<CCP>(CCP s1,CCP s2)
{
return (strcmp(s1,s2)>0)?s1:s2;
}

int main()
{
//调用实例:int Max<int>(int,int)
int i=Max(10,5);
//调用显示特化:const char* Max<const char*>(const char*,const char*)
const char* p=Max<const char*>("very","good");
cout<<"i:"<<i<<endl;
cout<<"p:"<<p<<endl;
}

程序正常编译运行结果:

i:10
p:very

在函数模板显示特化定义(Explicit Specialization Definition)中,显示关键字template和一对尖括号<>,然后是函数模板特化的定义。该定义指出了模板名、被用来特化模板的模板实参,以及函数参数表和函数体。在上面的程序中,如果不给出函数模板Max< T>在T为const char*时的特化版本,那么在比较两个字符串的大小时,比较的是字符串的起始地址的大小,而不是字符串的内容在字典序中先后次序。

除了定义函数模板特化版本外,还可以直接给出模板函数在特定类型下的重载形式(普通函数)。使用函数重载可以实现函数模板特化的功能,也可以避免函数模板的特定实例的失效。例如,把上面的模板特化可以改成如下重载函数。

typedef const char* CCP;
CCP Max(CCP s1,CCP s2)
{
return (strcmp(s1,s2)>0)?s1:s2;
}

程序运行结果和使用函数模板特化相同。但是,使用普通函数重载和使用模板特化还是有不同之处,主要表现在如下两个方面:
(1)如果使用普通重载函数,那么不管是否发生实际的函数调用,都会在目标文件中生成该函数的二进制代码。而如果使用模板的特化版本,除非发生函数调用,否则不会在目标文件中包含特化模板函数的二进制代码。这符合函数模板的“惰性实例化”准则。
(2)如果使用普通重载函数,那么在分离编译模式下,应该在各个源文件中包含重载函数的申明,否则在某些源文件中就会使用模板函数,而不是重载函数。

1.3类模板特化

类模板特化类似于函数模板的特化,即类模板参数在某种特定类型下的具体实现。考察如下代码。

#include <iostream>
using namespace std;

template<typename T>class A
{
T num;
public:
A()
{
num=T(6.6);
}
void print()
{
cout<<"A'num:"<<num<<endl;
}
};

template<> class A<char*>
{
char* str;
public:
A(){
str="A' special definition ";
}
void print(){
cout<<str<<endl;
}
};

int main()
{
A<int> a1; //显示模板实参的隐式实例化
a1.print();
A<char*> a2; //使用特化的类模板
A2.print();
}

程序输出结果如下:

A'num:6
A' special definition

2.模板偏特化

2.1概述

模板偏特化(template partitial specialization)是模板特化的一种特殊情况,指指定模板参数而非全部模板参数,或者模板参数的一部分而非全部特性,也称为模板部分特化。与模板偏特化相对的是模板全特化,指对所有的模板参数进行特化。模板全特化与模板偏特化共同组成模板特化。

模板偏特化主要分为两种,一种是指对部分模板参数进行全特化,另一种是对模板参数特性进行特化,包括将模板参数特化为指针、引用或是另外一个模板类。

2.2函数模板偏特化

假如我们有一个compare函数模板,在比较数值类型时没有问题,如果传入的数值的地址,我们需要两个数值的大写,而非比较传入的地址大小。此时我们需要对compare函数模板进行偏特化。考察如下代码:

#include <vector>
#include <iostream>
using namespace std;

//函数模板
template<typename T, class N> void compare(T num1, N num2)
{
cout << "standard function template" << endl;
if(num1>num2)
cout << "num1:" << num1 << " > num2:" << num2 <<endl;
else
cout << "num1:" << num1 << " <= num2:" << num2 << endl;
}

//对部分模板参数进行特化
template<class N> void compare(int num1, N num2)
{
cout<< "partitial specialization" <<endl;
if (num1>num2)
cout << "num1:" << num1 << " > num2:" << num2 << endl;
else
cout << "num1:" << num1 << " <= num2:" << num2 << endl;
}

//将模板参数特化为指针
template<typename T, class N> void compare(T* num1, N* num2)
{
cout << "new partitial specialization" << endl;
if (*num1>*num2)
cout << "num1:" << *num1 << " > num2:" << *num2 << endl;
else
cout << "num1:" << *num1 << " <= num2:" << *num2 << endl;
}

//将模板参数特化为另一个模板类
template<typename T, class N> void compare(std::vector<T>& vecLeft, std::vector<T>& vecRight)
{
cout << "to vector partitial specialization" << endl;
if (vecLeft.size()>vecRight.size())
cout << "vecLeft.size()" << vecLeft.size() << " > vecRight.size():" << vecRight.size() << endl;
else
cout << "vecLeft.size()" << vecLeft.size() << " <= vecRight.size():" << vecRight.size() << endl;
}

int main()
{
compare<int,int>(30,31);//调用非特化版本compare<int,int>(int num1, int num2)

compare(30,'1'); //调用偏特化版本compare<char>(int num1, char num2)

int a = 30;
char c = '1';
compare(&a,&c); //调用偏特化版本compare<int,char>(int* num1, char* num2)

vector<int> vecLeft{0};
vector<int> vecRight{1,2,3};
compare<int,int>(vecLeft,vecRight); //调用偏特化版本compare<int,char>(int* num1, char* num2)
}

程序输出结果如下:

standard function template
num1:30 <= num2:31
partitial specialization
num1:30 <= num2:1
new partitial specialization
num1:30 <= num2:1
to vector partitial specialization
vecLeft.size()1 <= vecRight.size():3

2.3类模板偏特化

类模板的偏特化与函数模板的偏特化类似。考察如下代码:

#include <vector>
#include <iostream>
using namespace std;

//类模板
template<typename T, class N> class TestClass
{
public:
static bool comp(T num1, N num2)
{
cout <<"standard class template"<< endl;
return (num1<num2) ? true : false;
}
};

//对部分模板参数进行特化
template<class N> class TestClass<int, N>
{
public:
static bool comp(int num1, N num2)
{
cout << "partitial specialization" << endl;
return (num1<num2) ? true : false;
}
};

//将模板参数特化为指针
template<typename T, class N> class TestClass<T*, N*>
{
public:
static bool comp(T* num1, N* num2)
{
cout << "new partitial specialization" << endl;
return (*num1<*num2) ? true : false;
}
};

//将模板参数特化为另一个模板类
template<typename T, class N> class TestClass<vector<T>,vector<N>>
{
public:
static bool comp(const vector<T>& vecLeft, const vector<N>& vecRight)
{
cout << "to vector partitial specialization" << endl;
return (vecLeft.size()<vecRight.size()) ? true : false;
}
};

int main()
{
//调用非特化版本
cout << TestClass<char, char>::comp('0', '1') << endl; 

//调用部分模板参数特化版本
cout << TestClass<int,char>::comp(30, '1') << endl; 

//调用模板参数特化为指针版本
int a = 30;
char c = '1';
cout << TestClass<int*, char*>::comp(&a, &c) << endl; 

//调用模板参数特化为另一个模板类版本
vector<int> vecLeft{0};
vector<int> vecRight{1,2,3};
cout << TestClass<vector<int>, vector<int>>::comp(vecLeft,vecRight) << endl;
}

程序输出结果:

standard class template
1
partitial specialization
1
new partitial specialization
1
to vector partitial specialization
1

3.模板类调用优先级

对主版本模板类、全特化类、偏特化类的调用优先级从高到低进行排序是:全特化类>偏特化类>主版本模板类。这样的优先级顺序对性能也是最好的。

但是模板特化并不只是为了性能优化,更多是为了让模板函数能够正常工作,最典型的例子就是STL中的iterator_traits。algorithm中大多数算法通过iterator对象来处理数据,但是同时允许以指针代替iterator对象,这是为了支持C-Style Array。如果直接操作iterator,那么为了支持指针类型,每个算法函数都需要进行重载,因为指针没有::value_type类型。为了解决这个问题,STL使用了iterator_traits对iterator特性进行封装,并为指针类型做了偏特化处理,算法通过它来操作iterator,不需要知道实际操作的是iterator对象还是指针。

template<typename IteratorClass> class iterator_traits
...
template<typename ValueType> class iterator_traits<ValueType*>
...
template<typename ValueType> class iterator_traits<ValueType const*>
...

后面两是针对指针类型的偏特化,也是偏特化的一种常见形式。

以上就是深入分析C++模板特化与偏特化的详细内容,更多关于C++模板特化与偏特化的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++模板之特化与偏特化详解

    前言 说到C++模板,这个已经不是什么新东西了,自己在实际开发中也用过:对于C++模板特化和偏特化,对于别人来说,已经不是什么新东西了,但是对于我来说,的确是我的盲区,那天在群里讨论这个问题,自己对于这部分确实没有掌握,又联想到在<STL源码剖析>一书中,对于此也是有着介绍.所以,今天就对此进行详细的总结,以备后忘. C++模板 说到C++模板特化与偏特化,就不得不简要的先说说C++中的模板.我们都知道,强类型的程序设计迫使我们为逻辑结构相同而具体数据类型不同的对象编写模式一致的代码,而无法抽

  • 解读C++编程中类模板的三种特化

    1.类模板显式特化 为了进行特化,首先需要一个通用的版本,称主模板.主模板使用了标准库堆算法.  堆 是一种线性化的树形结构,将一个值压入一个堆中, 实际上等于将该值插入到一个树形结构中;将一个值从堆中取出就等于移除并返回堆中最大值.但在处理字符的指针时会碰钉子.堆将按照指针的值进行组织. 我们可以提供一个显式特化版本解决此问题(例1)如果希望除了一个针对const char*的Heap外,还希望提供一个针对char *的Heap;(例2) //主模板 template <typename T>

  • C++ 类模板、函数模板全特化、偏特化的使用

    一.类模板全特化.偏特化 #pragma once #include <iostream> #include <map> template <typename T, typename U> class TC { public: TC() { std::cout << "泛化版本构造函数" << std::endl; } void funtest() { std::cout << "泛化版本成员函数&quo

  • 深入分析C++模板特化与偏特化

    1.模板特化 1.1概述 模板特化(template specialization)不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板的特化.模板特化有时也称之为模板的具体化,分别有函数模板特化和类模板特化. 1.2函数模板特化 函数模板特化是在一个统一的函数模板不能在所有类型实例下正常工作时,需要定义类型参数在实例化为特定类型时函数模板的特定实现版本.查看如下例子. #include <iostream> using namespace std; template<type

  • 深入分析:C++模板究竟会使代码膨胀吗

    今天和同事说到C++模板会使代码膨胀, 可同事觉得不会.同事的依据是: 如果模板会使代码膨胀, 那么ATL和WTL里为什么还要大量使用模板? 同样功能 ,ATL和WTL编译出的可执行文件可比MFC编译的要小的多.我当时一愣 ,事实确实如同事所说,难道模板会使代码膨胀的观点是错误的吗? MFC因为本身代码量和复杂性在那里, 所以它生成比较大的exe无可厚非.我们这里重点关注为什么ATL/WTL使用模板,但是却不会使生成的exe变大. 我们知道使用模板时, 同一模板生成不同的模板实类后会是多份代码

  • C++初阶学习之模板进阶

    目录 一.非模板类型参数 二.模板特化 1.函数模板特化 2.类模板特化 1)全特化 2)偏特化 三.模板分离编译 四.模板总结 总结 一.非模板类型参数 分类: 模板参数分类类型形参与非类型形参 概念: 1.类型形参: 出现在模板参数列表中,跟在class或者typename之类的参数类型名称 2.非类型形参: 用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用 示例: namespace cole { // 定义一个模板类型的静态数组 template<cla

  • C++模板全方位深入解读

    目录 1.泛型编程 2.函数模板 概念 函数模板的格式 函数模板的原理 函数模板的实例化 隐式实例化 显式实例化 模板参数的匹配原则 3.类模板 (1) 类模板的定义格式 (2) 类模板的实例化 4.非类型模板参数 5.模板特化 (1)函数模板的特化 (2)类模板的特化 全特化 偏特化 6.模板的分离编译 问题分析 1.泛型编程 如何实现一个通用的交换函数? 这点函数重载可以做到,比如一下Swap函数的重载,分别重载了俩种不同参数类型的Swap void Swap(int& x, int&

  • C++函数模板与类模板相同与不同介绍

    目录 1.模板 1.1何为模板 1.2C++的模板的形式有两种 1.3如何定义一个函数模板 1.4语法形式 1.5模板的编译机制 2.函数模板 2.1调用方式 2.2函数模板的特化与调用优先级 3.可变参函数模板 3.1概念 3.2代码实现(实现一个c中的printf的函数) 4.类模板 4.1类模板的定义形式 4.2代码实例 5.类模板中的特殊属性的初始化方式及继承与多态 5.1代码实例 5.2使用类模板去实现一个数据结构 5.3类模板的特化 5.4C++中类模板中的内嵌类 1.模板 1.1何

  • C++函数模板学习示例教程指南

    目录 C++函数模板学习指南 1. 函数模板的定义 2. 函数模板的使用 3. 函数模板的特化 4. 函数模板的偏特化 6. 非类型模板参数 7. 函数模板的局限性 总结 C++函数模板学习指南 C++函数模板是一种高效的代码复用机制,它允许我们定义一种可以用于多种类型的函数,而不必为每种类型都编写一个函数.本篇文章将介绍C++函数模板的基本使用.我们将逐步讨论函数模板的定义.使用.特化和偏特化. 1. 函数模板的定义 函数模板的定义基本语法如下: template <typename T>

  • C++中事件机制的简洁实现及需要放弃的特性

    事件模型是被广泛使用的好东西,但是C++标准库里没有现成的,其他实现又复杂或者不优雅,比如需要使用宏.现在VC11可以用在XP下了,那么就痛快的拿起C++11提供的先进设施组合出一个轻便的实现吧. 为了达到简洁的目的,需要放弃一些特性: 1.不支持判断函数是否已经绑定过(因为std::function不提供比较方法,自己实现function的话代码又变多了) 2.需要使用者接收返回的回调函数标识来移除事件绑定(原因同上) 3.事件没有返回值,不支持回调函数优先级.条件回调等事件高级特性(比如返回

随机推荐