您的位置:首页 > 编程语言 > C语言/C++

【c++】函数模板

2017-02-03 18:49 274 查看
何为泛型编程呢?简单的说就是,我们按照特定语法写代码,然后让编译器去具体实现这些代码。而函数模板,就是让编译器按照调用时的实参自动生成相应的函数版本.

定义和使用函数模板

格式如下:

template<typename AnyType>
returnType functionName(argument-list){
// do something
}


其中templatetypename是关键字。

Tips:

在c++98之前c++是使用class关键字来定义模板的,而在c++98开始使用typename关键字定义模板了。

示例:

现在假设我们想写一个swap函数来交换两个数的值

#include <iostream>
#include<cstring>
#include"test_h.h"
using std::string ;
using std::cout;
using std::endl;
//function declaration
template<typename T>
void swap(T& a,T& b);

int main(int argc, char** argv) {
int i1=19;
int i2=20;
double d1=19.0;
double d2=20.0;
cout<<i1<<endl;
cout<<i2<<endl;
cout<<d1<<endl;
cout<<d2<<endl;
cout<<"after transform"<<endl;
swap(i1,i2);
swap(d1,d2);
cout<<i1<<endl;
cout<<i2<<endl;
cout<<d1<<endl;
cout<<d2<<endl;

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

a=b;
b=temp;

}


编译器会根据swap()中的参数类型自动生成int和double版本的函数。

Node:

记住template在定义和声明中都是不能少的

模板函数使用的局限性

假设有如下模板

template<typename T>
void f(T a,T b){
//……
}


如果a,b是数组的话
a=b
是不可以的

如果a,b是结构的话
a>b
也是不可以的

所以模板编程有时候你要考虑一下参数的特殊性。这时候我们就要用到显示具体化

函数模板显式具体化

格式如下:

template <>returnTyoe function< Type>(Type, Type)


其中template <>为关键字,如果编译器可以根据参数列表推断模板类型那么< Type>中的Type为可选项

示例:

void swap(int a,int b);

template <>void swap<>(double, double);

template<typename T>

4000
void swap(T, T);


上卖弄三种分别为非模板函数,模板的显式具体化,模板函数

如果程序中同时定义了三种函数,会优先调用非模板函数,然后模板的显式具体化,最后模板函数

函数实例化

一开始定义的模板函数swap(T a,T b)它会告诉编译器生成函数的方式,但是还没有生成函数,而当调用swap(i1,i2)和swap(d1,d2)时就会生成相应的模板实例。模板的显式具体化也一样都是不会生成函数实例的,而是出现了具体类型之后再让编译器按照模板具体化创建函数实例。

在以前c++只能通过判断函数参数来实例化函数,这种叫做隐式实例化而现在的c++可以通过显式实例化了,格式如下

template returnType function<Type>(Type,Type);


上面的代码告诉编译器创建一个接受Type类型函数的实例,其中template为关键字,必须要写的。

示例:

template void swap<int>(int,int);//函数实例化

template<> void swap<int>(int,int);//模板具体化


上面的实例化告诉编译器使用swap模板生成一个接受int参数的函数实例,他已经在代码中了。

而模板显式具体化,告诉编译器当调用函数时如果参数为int时,就按照模板具体化定义的形式创建函数。

除了上面的显示实例化的手段,我们还可以在程序中手动使用函数模板来创建显示实例化,如下

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

a=b;
b=temp;

}
……
int i=19;
double d=19.0;
cout<<swap<double>(i,d)<<endl;//fun(Type)来显示创建函数实例


上卖弄的模板函数与swap(i,d)是不匹配的,因为两个参数是不同的,但是通过显式实例化swap(i,d)来用模板 swap(T a,T b)创建double版本的函数,然后int的值会自动转换为double,以便函数使用

函数模板的调用

当同时有非模板函数,模板函数,模板显示具体化时定义时。会优先调用非模板函数,然后模板的显示具体化,最后模板函数

void f(int);                //#1

float f(float,float=3);         //#2

void f(char)                //#3

char * f(const char *)          //#4

char f(const chat *)            //#5

template <class T>void f(const T &)   //#6

template <class T>void fT *)    //#7


如果调用

f('B')


上面#3,#5,#6都是原型匹配,但是#3,#5优先于#6,#6优先于#1

decltype

template<typename T>
void f(T a,T b){

ab=a+b;//ab是什么类型的呢?
}


所以c++11引入了关键字decltype来解决这个问题

int x;
decltype(x) y;//y is int


decltype(x+y) xy;//xy is x+y type
xy=x+y;


decltype(x+y)xy=x+y;//xy is x+y type


上面是三种decltype的简单使用。而对于如下标准格式

decltype(expression) var;

编译器会编译一个核对表,核对表的简化版如下

第一步:如果expresssion是一个没有用括号的标识符,则var的类型与该标识符相同,包括const等修饰符

double x=19.0;
double y=19.0
double &rx=x;
const double *pd;
decltype (x) w;//w is double
decltype (rx) u;
//u is double &
decltype (pd) v ;
//v const double *


第二步:如果experssion是一个函数调用,var是的类型与函数返回值一样

int f(int);
decltype(   f(3)) m//m is int


Note:

这实际不会调用函数,编译器会根据函数原型判断返回值类型

第三步:如果expression是一个左值,则var为指向类型的引用。这好像意味着前面w应为引用类型,因为x是一个左值,但是这种情况下已经在第一步处理过了。要进入第三步expression不能是未用括号括起来的标识符

double xx=19.0;
decltype((xx))r2=xx;//r2 is double &
devltype(xx)w=xx//w is double


第四步:如果前面条件都不满足,则var的类型与expression类型相同

int j=3;
int& k=j;
int& n=j;
decltype(j+6) i1;//i1 is int
decltype(100L) i2;
//i2 is long
decltype(k+n) i3;
//i3 is int


c++11后置返回类型

template<typename T>
Type f(T a,T b){
return a+b;//a+b的返回上什么类型呢?
}


decltype (a+b) 不行吗?因为这时候还没有定义a,b,编译器不知道a,b是什么。c++11为auto新增了语法功能来解决这个问题,如下

auto f(int i1,int i2)->double;


auto告诉编译器返回类型在后面写着。他可以和decltype 连用

auto  f(int i1,int i2)->decltype(x+y);


这样就完成了返回类型的自动换了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: