1

C++函数模板 - 土狗送大礼

 1 year ago
source link: https://www.cnblogs.com/qsnn/p/16370860.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

C++函数模板

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

在使用模板函数时,编译器根据实际的类型生成相应的函数定义。

重载的模板

并非所有的类型都使用相同的算法,可以像重载常规函数那样重载模板函数定义。

template<typename T>
void Swap(T &a ,T &b); //#1

template<typename T>
void Swap(T *a ,T *b,int n);//#2 最后一个参数是具体类型

int main()
{
    int i =10,j=20;
    Swap(i,j);//使用#1
    
    const int Lim = 8;
    int d1[Lim]={0,1,2,3,4,5,6,7};
    int d2[Lim]={7,6,5,4,3,2,1,0};
    Swap(d1,d2,Lim);//使用#2
}
template<typename T>
void Swap(T &a ,T &b)
{
    T temp;
    temp = a;
    a = b;
    b = temp;
}

template<typename T>
void Swap(T *a ,T *b,int n)
{
    T temp;
    for(int i=0;i<n;i++)
    {
        temp =a[i];
        a[i]=b[i];
        b[i]=temp;
    }
}

模板局限性

某些时候,类型T的相应操作只适用于数组,如果T为结构体则模板函数便不成立

同样,如if(a>b),如果T为结构,则>便不成立

解决方案:

  1. 重载运算符号
  2. 为特定类型提供具体化模板定义

显示具体化

当编译器找到与函数调用匹配的具体化定义时,将使用该定义,不再寻找模板。

  • 对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及各自的重载版本。
  • 显示具体化的原型和定义以template<>开头,并通过名称来指出类型
  • 调用顺序是:非模板函数>具体化模板函数>模板函数
void Swap(job& ,job&);

template <typename T>
void Swap(T&,T&);

template<> void Swap<job>(job& ,job&);//显示具体化
//Swap<job>中<job>是可选的,因为函数的参数类型表明,这是job的一个具体化,所以也可以这样写:
template<> void Swap(job& ,job&);

实例化和具体化

注意:函数模板并不会生成函数定义,他只是生成一个用于生成函数定义的方案,编译器使用模板为特定的类型生成函数定义时,得到的是模板实例。

template<typename T>
void Swap(T &a ,T &b);

int a =10,b=20;
Swap(a,b);//因为提供了int类型的参数,所以自动生成了int类型的模板实例。这样是==隐式实例化==
//也可以直接命令编译器创建特定的实例
//显示实例化
template void Swap<int>(int &,int &);//使用Swap()模板生成int类型的函数定义

//显示具体化
template<> void Swap<int>(int& ,int&);
template<> void Swap(int& ,int&);
//区别在于:具体化是不使用Swap()模板函数生成函数定义,而是使用专门为int类型显示定义的函数定义
//简单的理解,具体化是对函数的声明,而实例化是对模板函数的使用
template<typename T>
T Add(T a,T b)
{
    return a+b;
}

int m=6;
double x=10.5;
Add<double>(x,m); //与Add(x,m)不匹配,因为一个是int一个是double
				  //通过Add<double>实例化,可强制将m转为double

//但是同样的对Swap便不能成功,因为Swap中使用的是引用类型
Swap<double>(m,x);//double& 不能指向int
//使用案例
template <typename T>
void Swap(T &,T &);

template<> void Swap<job>(job&,job&);//具体化
int mian()
{
    template void Swap<char>(char& ,char&);
    
    short a,b;
    Swap(a,b);//隐式实例化
    
    job n,m;
    Swap(n,m);//显示具体化
    
    char g,h;
    Swap(g,h);//显示实例化
}

模板函数类型的确定

template<class T1,class T2>
void fun(T1 x,T2 y)
{
    ?type? s=x+y; //因为是模板函数,此时?type?类型不确定
}

C++11增加decltype关键字

template<class T1,class T2>
void fun(T1 x,T2 y)
{
    decltype(x+y) s=x+y; //s类型与x+y的类型一致
}

使用decltype(expression) var 的步骤:

  1. 如果expression没有用括号括起来,则var与expression类型相同,包括const等限定符
double x =5.5;
double& z =x;
const double* pd;
decltype(x) w; //w为double类型
decltype(z) u; //u为double& 类型
decltype(pd) v; //v为const double* 类型
  1. 如果expression是一个函数调用,则var与返回值类型相同。并不会实际调用函数,编译器通过查看原型来确定返回值类型
  2. 如果expression是一个左值,则var为指向其类型的引用。常见的情况如下:
double x = 4.5;
decltype((x)) r = x;//r是double&类型
decltype(x) r = x;//r是double类型

//括号不会改变expression的值和左值性
//可理解为加括号仅仅是decltype声明引用的一种方式
  1. 如果前3条都不满足,则var与expression类型相同
int j=3;
int &k=j;
int &n=j;

decltype(j+6) x; //x是int
decltype(k+n) y;//y是int ,虽然k和n是引用,但是k+n不是引用是2个int的和

如果多次声明,可以结合typedefdecltype

typedef decltype(x+y) xytype;
xytype z = x+y;
xytype arr[10];

但是某些需定义返回值类型的函数模板任然不能得到解决,如:

template<class T1,class T2>
?type? fun(T1 x,T2 y) //此时无法确定类型
{
    return x+y;
}

C++新增语法auto h(int x,float y) -> double,这称为后置返回类型,auto是一个占位符

template<class T1,class T2>
auto fun(T1 x,T2 y)->decltype(x+y) //后置类型使用decltype
{
    return x+y;
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK