稍微深入函数模板

C++中的函数新特性(二)

  1. 浅谈函数模板和泛型编程
  2. 函数模板的实现原理
  3. 扩展:模板重载简介
  4. 局限性:模板的具体化
  5. 局限性:模板实例的选择
  6. 函数的选择
  7. 局限性:类型的判定

函数模板

浅谈函数模板与泛型编程

泛型编程
通过一种语言机制
实现一个通用的方法(标准容器库)
通用是指:每一种数据类型都能用

函数模板怎么实现泛型编程呢?

上次的例子:
如果我要打一个交换两个int变量的函数
我可以这样

void swap(int & a, int & b)
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}

如果通过函数模板来实现泛型编程
则可以这样

template <typename T >
void swap(T & a, T & b)
{
    T temp;
    temp = a;
    a = b;
    b = temp;
}

关键字:template(模板)
关键字:typename

第一行,指出这是一个函数模板
一个可替代的变量类型,我们暂且命名为T
然后后面函数的定义
就用T来代替具体的类型
之后我们的函数调用
就可以用这一个swap函数,去交换int变量
去交换double变量

实际上函数模板就是在编译的时候
将T换成所需要的类型
就像 typedef T int ;一样

所以我们在调用这个函数的时候
可以这样调用

double a,b;
swap(a,b);
int c,d;
swap(c,d);

然后就达到了泛型编程的目的
对,无论什么类型,都可以调用
吗?



当然,事情并没有那么简单。
数据类型,远远不止这几种基本类型
还有结构体,还有类
还有……………………
这个在后面的局限性里拓展一下

函数模板的实现原理

template <typename T >
void swap(T & a, T & b)
{
    T temp;
    temp = a;
    a = b;
    b = temp;
}

函数模板的语法
template是模板的关键字
typename是在模板中说明类型的关键字
template < typename T >
在第一行中,先声明了一个类型参数T
然后在函数的具体定义中,
把需要用到该类型的全部用T

原理:
在编译过程
编译器会自动根据函数的调用情况
知道需要使用哪种类型的函数
然后就会
直接把T替换成对应的类型
生成一个新函数

所以,在编译过程,
模板就会变成真正的函数
(这个过程叫做模板的实例化)
在编译过后,程序就没有模板了
只有对应的函数

模板的重载

同函数一样,模板也是可以重载的
(还记得什么叫函数的重载吗?)
(C++中允许同名函数的存在)
(虽然同名,不过会根据函数的特征标选择)
(所以不会产生冲突)

模板重载
同名函数模板
在C++中是允许存在的
前提是模板的参数不同

先放个例子

template <typename T >
T add(T a, T b)
{
    return a+b;
}

对这个模板,
我们可以使两个相同类型的变量相加
然后返回他们的和

template <typename T1, typename T2 >
T1 add(T1 a, T2 b)
{
    return a+b;
}

对这个模板
我们可以使两个具有不同类型的变量相加
并且返回第一个变量所具有的类型
当然,我们还可以这样

template <typename T,int t >
T add(T a, T b)
{
    return a+b+t;
}

而这个模板,我们可以在模板参数里
放入一个int类型的整数
然后相加的时候加多一个数

模板局限性1:

如果模板对某一种类型不合适
可以自己另外定义一个函数代替模板吗

假设我现在有这样一个模板

template <typename T >
void swap(T & a, T & b)
{
    T temp;
    temp = a;
    a = b;
    b = temp;
}

然后我有这样一种
用结构体定义的数据类型

struct person{
    char name[40];
    int age;
};

我能否这样?

person p1,p2;
// after initialization
swap( p1, p2 );

虽然C++允许结构体的赋值
可是,如果我只想交换age这一个变量呢
只想交换年龄而不改名可以吗?

模板的显式具体化技术

我们想做的事情就是
想让一个模板,对其余的所有类型都适用
唯独用person类型的时候
我想另外定义一个而不使用模板的定义
这里,我们使用模板的显式具体化技术

具体化:
就是对某一种类型给出具体的定义

这里我先给出person类型的显式具体化例子

//函数原型
template <> 
void swap<person>(person & a, person & b);
//函数定义
template <> 
void swap<person>(person & a, person & b)
{
    int temp;
    temp = a.age;
    a.age = b.age;
    b.age = temp;
}

对比普通模板

//函数原型
template <typename T >
void swap(T & a, T & b);
//函数定义
template <typename T >
void swap(T & a, T & b)
{
    T temp;
    temp = a;
    a = b;
    b = temp;
}

区别1:template还在
但是后面的<>中可自定义的类型参数没有了
也就是说没有了可自定义的类型参数
区别2:函数名
函数名后面加上了< person >
表明这是该模板下该类型的定义
区别3:形参列表及函数定义
没有了自定义的类型参数
后面的一切操作
都是由具体的类型完成

函数的具体化
使得在调用函数的时候
能够根据类型,选择使用模板函数
还是用模板函数下的具体化

模板局限性2:

假设有这样一种模板函数

template <typename T >
T add(T a, T b)
{
    return a+b;
}

问题描述
使这个模板函数这样调用

int   a = 2;
double d = 3.0;
add(a,d);

模板函数该如何选择类型?

这里我们需要用到模板的另一种语法

模板的实例化技术

什么是实例化?

就是从模板变成真正的函数
实例化分为
隐式实例化和显式实例化

隐式实例化

在前面的原理中我们说到
模板在编译过程就会变成真正的函数
在编译后
代码是没有模板这一种东东的
模板,已经变成了一个一个的函数
如果以上面的swap函数为例
我一旦调用swap函数

int a = 2;
int b = 3;
add(a,b);

编译器便会知道我需要int类型的add函数
然后就会帮我通过模板
生成int类型函数

int add(int  a, int  b)
{
    return a+b;
}

这样,就是函数模板的一个实例化
由于我并没有告诉编译器要实例化
只是编译器比较聪明知道应该要这么做
就叫隐式实例化

显式实例化

如果要明确的告诉编译器
需要实例化某一个类型的函数
我可以这样做

template void add<int>(int , int );

通过这一条语句
编译器便会知道需要生成int类型的swap函数
(无论我后面有没有调用)

还可以通过这样的方法
在函数调用的时候调用显式实例化的一个函数

int x = 3;
double y = 3.2;
double sum = add<double>(x,y);

从而解决模板函数调用模糊不清
不知道该调用谁的问题

提出几个问题

  1. 所有类型都可以直接相加吗?
    如:我用前面的add函数模板,去相加字符串可以吗?

其实是不可以的
但是如果可以自己为字符串重载运算符+
(也就是自己定义一个适用于字符串的+运算符)
也许就可以了

  1. 如果我在模板中不知道该返回什么类型的参数
template <typename T1, typename T2 >
???? add(T1 a, T2 b)
{
    return a+b;
}

上面的程序,我可不能确定a+b的类型呀
可能是T1,也可能是T2
这可不能随便定下来呀
能不能在运行的时候根据我的参数的输入决定呢?

这个又能扯到很多东西……下次再说说

我还是希望能够快点讲到类……

这样又有一堆东西可以写了……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值