[转]C++函数模板与模板函数
1.1函数模板的声明
函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计。它的最大特点是把函数使用的数据类型作为参数。
函数模板的声明形式为:
template<typename 数据类型参数标识符>
<返回类型><函数名>(参数表)
{
函数体
}
其中,template是 定义模板函数的关键字;template后面的尖括号不能省略;typename(或class)是声明数据类型参数标识符的关键字,用以说明它后面的标 识符是数据类型标识符。这样,在以后定义的这个函数中,凡希望根据实参数据类型来确定数据类型的变量,都可以用数据类型参数标识符来说明,从而使这个变量 可以适应不同的数据类型。例如:
template<typename T>
T fuc(T x, int y)
{
T x;
//……
}
如果主调函数中有以下语句:
double d;
int a;
fuc(d,a);
则系统将用实参d的数据类型double去代替函数模板中的T生成函数:
double fuc(double x,int y)
{
double x;
//……
}
函数模板只是声明了一个函数的描述即模板,不是一个可以直接执行的函数,只有根据实际情况用实参的数据类型代替类型参数标识符之后,才能产生真正的函数。
关键字typename也可以使用关键字class,这时数据类型参数标识符就可以使用所有的C++数据类型。
1.2.模板函数的生成
函数模板的数据类型参数标识符实际上是一个类型形参,在使用函数模板时,要将这个形参实例化为确定的数据类型。将类型形参实例化的参数称为模板实参,用模板实参实例化的函数称为模板函数。模板函数的生成就是将函数模板的类型形参实例化的过程。例如:
使用中应注意的几个问题:
⑴ 函数模板允许使用多个类型参数,但在template定义部分的每个形参前必须有关键字typename或class,即:
template<class 数据类型参数标识符1,…,class 数据类型参数标识符n>
<返回类型><函数名>(参数表)
{
函数体
}
⑵ 在template语句与函数模板定义语句<返回类型>之间不允许有别的语句。如下面的声明是错误的:
template<class T>
int I;
T min(T x,T y)
{
函数体
}
⑶ 模板函数类似于重载函数,但两者有很大区别:函数重载时,每个函数体内可以执行不同的动作,但同一个函数模板实例化后的模板函数都必须执行相同的动作。
2 函数模板
经常有碰到函数模块的应用,很多书上也只是略有小讲一下,今天又狂碰到函数模块,无奈特地找来C++编程经典<<C++ Primer>>翻阅一遍,终于有所全面了解.....
C++函数模块基础知识:
一. 问题:
强类型语言要求我们为所有希望比较的类型都实现一个实例
int min( int a, int b ) {
return a < b ? a : b;
}
double min( double a, double b ) {
return a < b ? a : b;
}
有一种方法可替代为每个min()实例都显式定义一个函数的方法这种方法很有吸引力但是也很危险.那就是用预处理器的宏扩展设施例如 : #define min(a,b) ((a) < (b) ? (a) : (b))
在复杂调用的情况下,它的行为是不可预期的,这是因为它的两个参数值都被计算两次. 一次是在a 和b 的测试中另一次是在宏的返回值被计算期间.
#include <iostream>
#define min(a,b) ((a) < (b) ? (a) : (b))
const int size = 10;
int ia[size];
int main() {
int elem_cnt = 0;
int *p = &ia[0];
// 计数数组元素的个数
while ( min(p++,&ia[size]) != &ia[size] )
++elem_cnt;
cout << "elem_cnt : " << elem_cnt
<< "\texpecting: " << size << endl;
return 0;
}
执行该程序的结果是下面不正确的计算结果: elem_cnt : 5 expecting: 10
min()的宏扩展在这种情况下会失败因为应用在指针实参p 上的后置递增操作随每次扩展而被应用了两次
二 . 解决办法:
函数模板提供了一种机制通过它我们可以保留函数定义和函数调用的语义在一个程序位置上封装了一段代码确保在函数调用之前实参只被计算一次.
函数模板提供一个种用来自动生成各种类型函数实例的算法程序员对于函数接口参数和返回类型中的全部或者部分类型进行参数化(parameterize)而函数体保持不变.
下面是min()的函数模板定义
template <class Type>
Type min( Type a, Type b ) {
return a < b ? a : b;
}
三. 具体操作
关键字template 总是放在模板的定义与声明的最前面关键字后面是用逗号分隔的模板参数表(template parameter list)它用尖括号<> 一个小于号和一个大于号括起来该列表是模板参数表不能为空模板参数可以是一个模板类型参数(template typeparameter)它代表了一种类型也可以是一个模板非类型参数(template nontype parameter)它代表了一个常量表达式模板类型参数由关键字class 或typename 后加一个标识符构成在函数的模板参数表中这两个关键字的意义相同。
模板非类型参数由一个普通的参数声明构成模板非类型参数表示该参数名代表了一个潜在的值而该值代表了模板定义中的一个常量例如size 是一个模板非类型参数它代表arr 指向的数组的长度
template <class Type, int size>
Type min( Type (&arr) [size] );
当函数模板min()被实例化时size 的值会被一个编译时刻已知的常量值代替。函数定义或声明跟在模板参数表后除了模板参数是类型指示符或常量值外函数模板的定义看起来与非模板函数的定义相同
template <class Type, int size>
Type min( const Type (&r_array)[size] )
{
/* 找到数组中元素最小值的参数化函数 */
Type min_val = r_array[0];
for ( int i = 1; i < size; ++i )
if ( r_array[i] < min_val )
min_val = r_array[i];
return min_val;
}
在程序的运行过程中Type 会被各种内置类型和用户定义的类型所代替而size 会被各种常量值所取代这些常量值是由实际使用的min()决定的记住一个函数的两种用法是调用它和取它的地址
当 一个名字被声明为模板参数之后它就可以被使用了一直到模板声明或定义结束为止模板类型参数被用作一个类型指示符可以出现在模板定义的余下部分它的使用方式 与内置或用户定义的类型完全一样比如用来声明变量和强制类型转换模扳非类型参数被用作一个常量值可以出现在模板定义的余下部分它可以用在要求常量的地方或 许是在数组声明中指定数组的大小或作为枚举常量的初始值
四.几点注意
① 如果在全局域中声明了与模板参数同名的对象函数或类型则该全局名将被隐藏在下面的例子中tmp 的类型不是double 是模板参数Type
typedef double Type;
template <class Type>
Type min( Type a, Type b )
{
// tmp 类型为模板参数 Type
// 不是全局 typedef
Type tmp = a < b ? a : b;
return tmp;
}
② 在函数模板定义中声明的对象或类型不能与模板参数同名
template <class Type>
Type min( Type a, Type b )
{
// 错误: 重新声明模板参数 Type
typedef double Type;
Type tmp = a < b ? a : b;
return tmp;
}
③ 模板类型参数名可以被用来指定函数模板的返回位
// ok: T1 表示 min() 的返回类型
// T2 和 T3 表示参数类型
template <class T1, class T2, class T3>
T1 min( T2, T3 );
④ 模板参数名在同一模板参数表中只能被使用一次,但是模板参数名可以在多个函数模板声明或定义之间被重复使用
// 错误: 模板参数名 Type 的非法重复使用
template <class Type, class Type>
Type min( Type, Type );
// ok: 名字 Type 在不同模板之间重复使用
template <class Type>
Type min( Type, Type );
template <class Type>
Type max( Type, Type );
⑤ 如果一个函数模板有一个以上的模板类型参数则每个模板类型参数前面都必须有关键字class 或typename
// ok: 关键字 typename 和 class 可以混用
template <typename T, class U>
T minus( T*, U );
// 错误: 必须是 <typename T, class U> 或 <typename T, typename U>
template <typename T, U>
T sum( T*, U );
⑥ 为了分析模板定义编译器必须能够区分出是类型以及不是类型的表达式对于编译器来说它并不总是能够区分出模板定义中的哪些表达式是类型例如如果编译器在模板定义中遇到表达式Parm::name 且Parm 这个模板类型参数代表了一个类那么name 引用的是Parm 的一个类型成员吗.
template <class Parm, class U>
Parm minus( Parm* array, U value )
{
Parm::name * p; // 这是一个指针声明还是乘法乘法
}
编译器不知道name 是否为一个类型因为它只有在模板被实例化之后才能找到Parm 表示的类的定义为了让编译器能够分析模板定义用户必须指示编译器哪些表达式是类型表达式告诉编译器一个表达式是类型表达式的机制是在表达式前加上关键字typename 例如如果我们想让函数模板minus()的表达式Parm::name 是个类型名因而使整个表达式是一个指针声明我们应如下修改
template <class Parm, class U>
Parm minus( Parm* array, U value )
{
typename Parm::name * p; // ok: 指针声明
}
关键字typename 也可以被用在模板参数表中以指示一个模板参数是一个类型
⑦ 如同非模板函数一样函数模板也可以被声明为inline 或extern 应该把指示符放在模板参数表后面而不是在关键字template 前面
// ok: 关键字跟在模板参数表之后
template <typename Type>
inline
Type min( Type, Type );
[转]C++函数模板与模板函数的更多相关文章
- 3.2 STL中的函数对象类模板
*: STL中有一些函数对象类模板,如下所示: 1)例如要求两个double类型的x 和y 的积,可以: multiplies<double>()(x,y); 该表达式的值就是x*y的值. ...
- [c++][语言语法]函数模板和模板函数 及参数类型的运行时判断
参考:http://blog.csdn.net/beyondhaven/article/details/4204345 参考:http://blog.csdn.net/joeblackzqq/arti ...
- c++特性:指向类成员的指针和非类型类模板参数和函数指针返回值 参数推导机制和关联型别
一.c++允许定义指向类成员的指针,包括类函数成员指针和类数据成员指针 格式如下: class A { public: void func(){printf("This is a funct ...
- C++:函数模板与模板函数
6.1 模板的概念 C++允许用同一个函数定义函数,这些函数的参数个数和参数类型不同.例如求最大值的max函数, int max(int x,int y) { return (x>y ...
- C++ template学习一(函数模板和模板函数)
函数模板和模板函数(1)函数模板函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计.它的最大特点是把函数使用的数据类型作为参数.函数模板的声明形式为:templat ...
- ThinkPHP第五天(提交类型判定常量IS_POST等,错误页面种类,Model实例化方式,模板中使用函数,foreach循环,模板中.语法配置)
1.IS_GET.IS_POST.IS_PUT.IS_DELETE.IS_AJAX常量,方便快捷实现各个判断. 在Action类中还可以使用$this->isPost()等进行判断. 2.错误页 ...
- C++ - 模板类模板成员函数(member function template)隐式处理(implicit)变化
模板类模板成员函数(member function template)隐式处理(implicit)变化 本文地址: http://blog.csdn.net/caroline_wendy/articl ...
- C++模板学习:函数模板、结构体模板、类模板
C++模板:函数.结构体.类 模板实现 1.前言:(知道有模板这回事的童鞋请忽视) 普通函数.函数重载.模板函数 认识. //学过c的童鞋们一定都写过函数sum吧,当时是这样写的: int sum(i ...
- [C++基础]那些容易被混淆的概念:函数/数组指针-指针函数/数组,类/函数模板-模板类/函数
函数指针-指针函数 函数指针的重点是指针.表示的是一个指针,它指向的是一个函数.eg: int (*pf)(); 指针函数的重点是函数.表示的是一个函数,它的返回值是指针.eg: int* fun() ...
- C++ 函数模板一(函数模板定义)
//函数模板定义--数据类型做参数 #include<iostream> using namespace std; /* 函数模板声明 1.函数模板定义由模板说明和函数定义组成,并且一个模 ...
随机推荐
- Oracle 数据库导出时 EXP-00008;ORA-00904
问题是客户端和服务器端版本问题,我本地是11g,而服务器端是10g. 规则1:低版本的exp/imp可以连接到高版本(或同版本)的数据库服务器,但高版本的exp/imp不能连接到低版本的数据库服务器. ...
- 使用“\n\t”将多行字符串拼接起来
以前js拼接字符串有好多 \n \t 不使用ES6 使用"\n\t"将多行字符串拼接起来: var roadPoem = 'Then took the other, as just ...
- 【bzoj1070】[SCOI2007]修车 最小费用流
原文地址:http://www.cnblogs.com/GXZlegend/p/6798411.html 题目描述 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的 ...
- 【BZOJ 4151 The Cave】
Time Limit: 5 Sec Memory Limit: 256 MBSec Special JudgeSubmit: 293 Solved: 144[Submit][Status][Di ...
- 忽略node_modules目录
如上图所示添加node_modules目录到忽略文件列表里面,点击应用就可以了.
- hihoCoder 1015KMP
#include <iostream> #include <algorithm> #include <stdio.h> #include <math.h> ...
- 汕头市队赛 SRM1X T1
木之本樱 背景 “西瓜是可以种在树上的!”——木之本樱 描述 空地上,一排排的西瓜树拔地而起. 魔法世界里,空地是无限大的.所有的树排成了n条直线,每条直线也是向左右两端无限延伸的. 由于自己姓木(之 ...
- PriorityQueue详解(一)
在Java SE 5.0中,引入了一些新的Collection API,PriorityQueue就是其中的一个.今天由于机缘巧合,花了一个小时看了一下这个类的内部实现,代码很有点意思,所以写下来跟大 ...
- quartz 调度
1.POI文件中导入包 <dependency> <groupId>org.quartz-scheduler</groupId> <artifact ...
- 转: wireshark过滤规则
转: http://blog.sina.com.cn/s/blog_48a0f2740100ka71.html WireShark过滤语法 1.过 滤IP,如来源IP或者目标IP等于某个IP 例子: ...