c++函数模板

一. 使用函数模板

先看一组代码,功能是交换两个数:

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

void swapp(int *p, int *q)
{
    int temp;
    temp = *p;
    *p = *q;
    *q = temp;
}

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

如果要交换两个double类型的值,一种方法是把int替换成double,定义一个新的方法。但是这样不仅容易出错,还要花费额外的时间来维护检查。所以需要用到函数模板:


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;
    }
}

三. 函数模板的局限性

如果函数模板里出现了<,>,*等运算,对于指针,数组或者结构体的类型来说,并不适用。

四. 显示具体化

同样的模板函数名下,需要针对某些类型做出不同的算法。因此c++标准给出了显示具体化的机制:

  • 一个函数名,可以同时存在非模板函数,模板函数,显示具体化模板函数以及它们的重载版本;
  • 显示具体化的原型要以template<>开头,并通过名称指出类型;
  • 编译器选择的优先级:非模板函数 > 具体化模板函数 > 模板函数;

以下是交换函数的非模板函数,模板函数和具体化的原型:

struct job{
    char name[40];
    double salary;
    int floor;
};

// non template function prototype
void Swap(job &, job &);

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

// explicit specialization for type job
template <>
void Swap<job>(job &, job &); // 只交换salary和floor成员

五. 显式实例化

先了解这几个术语:显式具体化,隐式实例化,显式实例化。

函数模板或者具体化模板都是给出一个函数定义的方案。编译器使用模板生成函数定义的时候叫做实例化。编译器根据参数类型调用相应模板生成函数定义叫做隐式实例化。

显式实例化就是命令编译器直接生成特定类型的函数定义,语法是:

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

还可以这么写:

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

...

int m = 6;
double n = 12.1;
cout << template<double>add(m, n) << endl;

显示实例化是指显式地告诉编译器为特定类型生成模板实例,而不是等待编译器在使用模板时自动生成 。这在某些情况下可能会提高编译速度或减少代码体积,因为编译器可以避免多次生成相同模板实例。
显示具体化用于处理特定类型的特殊需求,而显示实例化则用于优化编译过程。

隐式实例化、显式实例化和显式具体化统称为具体化

六. c++11标准对函数模板的一些改进

decltype声明

假设在c++98标准中有这样一个函数模板:
template<class T1, class T2>
void fd(T1 x, T2 y)
{
    ?Type? xpy = x + y;
}

xpy的类型是无法确定的。因为double+int = double,int + short = int, char + short = int,如果是结构体或者类那么更不能确定了。

c++11标准给出了decltype关键字:

template<class T1, class T2>
void fd(T1 x, T2 y)
{
    decltype(x+y) xpy = x + y;
}

那么decltype(expression) var类型是什么?

编译器会按以下规则推断var的类型:

a. 如果expression是一个没有括号的标识符,那么var类型和标识符相同(包括const限定);

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

b. 如果expression是一个函数调用,那么var类型和函数返回类型相同;

long indeed(int);
decltype (indeed(3)) m; // m is type int

c. 如果expression是一个左值,那么var类型是expression类型的引用;

double xx = 4.4;
decltype ((xx)) r2 = xx; // r2 is double &
decltype(xx) w = xx; // w is double 匹配条件a

d. 如果以上条件都不满足,那么var类型和expression类型相同;

int j = 3;
int &k = j;
int &n =j;
decltype(j+6) i1; // i1 type int
decltype(100L) i2; // i2 type long
decltype(k+n) i3; // i3 type int;
虽然 k n 都是引用,但表达式 k+n 不是引用;它是两个 int 的和,因此类型为int
还可以结合typedef一起使用:
template<class T1, class T2>
void ft(T1 x, T2 y)
{
    typedef decltype(x + y) xytype;
    xytype xpy = x+y;
    xytype arr[10];
    xytype & rxy = arr[2];
}

后置返回类型

template<class T1, class T2>
?type? gt(T1 x, T2 y)
{
    return x + y;
}

编译器无法预知返回类型,因为x和y不在作用域内了。

因此有了一种新的语法声明函数返回类型:

double h(double a, float b);

auto h(double a, float b)->double;

template<class T1, class T2>
auto gt(T1 x, T2 y)->decltype(x+y)
{
    return x+y;
}
现在, decltype 在参数声明后面,因此 x y 位于作用域内,可以使用它们。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值