函数模板

1、简单函数模板

函数模板是通用的函数描述,也就是说他们使用通用类型来定义函数,其中的通用类型可用具体的类型替换。通过将类型作为参数传递给模板,可使编译器生成该类型的函数。

比如我们定义了一个函数,函数实现交换两个int变量的值。

void swap(int &a, int &b);

如果我们要实现一个交换两个double变量的值,如何做呢,我们可以重新复制一份代码,更改参数类型和实现完成函数重载。若要交换两个char类型的值,同样复制一份代码。这样做很麻烦,还有可能出错。这里我们可以使用模板的方式来实现上述功能。

模板的形式如下:

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

第一行指出,要建立一个模板,并将类型名命名为Any。关键字template和class是必需的,class也可以使用typename来代替。另外,必须使用尖括号。类型的名字可以任意选择。余下的代码则描述如何交换两个Any值。这里要注意的是,模板并不会创建任何函数,而只是高速编译器如何定义函数。需要交换int类型的函数时,编译器将按照模板模式创建这样的函数,并用int代替Any。需要交换double类型的函数时,编译器将按照模板模式创建这样的函数,并用double代替Any。

我们来写个简单的例程

#include <iostream>                                                                                                                                              
template <class any>
void Swap(any &a, any &b);

int main()
{
        using namespace std;
        int i = 10; 
        int j = 20; 
        cout << "i = " << i << " j = " << j << endl;
        cout << "after Swap" << endl;
        Swap(i,j);
        cout << "i = " << i << " j = " << j << endl;

        double x = 1.1;
        double y = 9.9;
        cout << "x = " << x << " y = " << x << endl;
        cout << "after Swap" << endl;
        Swap(x,y);
        cout << "x = " << x << " y = " << y << endl;

}

template <class any>
void Swap(any &a, any &b) 
{
        any tmp;
        tmp = a;
        a = b;
        b = tmp;
}

执行结果

2、重载的模板

模板也是可以重载的,可以像重载常规函数定义那样重载模板定义。和常规的重载一样,被重载的模板的函数特征标必须不同。如上面的例子中,假如我们要交换并不仅仅是一个数字,而是一个数组呢?

#include <iostream>                                                                                                                                              

template <class any>
void Swap(any &a, any &b) 
{
        any tmp;
        tmp = a;
        a = b;
        b = tmp;
}
template <class any>
void Swap(any a[], any b[], int n)
{
        any temp;
        int i;
        for(i = 0; i < n; i++) {
                temp = a[i];
                a[i] = b[i];
                b[i] = temp;
        }
}
#define NUM 8
void Show(int a[])
{
        using namespace std;
        cout << a[0] << a[1] << "/";
        cout << a[2] << a[3] << "/";
        for (int i = 4; i < NUM; i++)
                cout << a[i];
        cout << endl;
}


int main()
{
        using namespace std;
        int i = 10;
        int j = 20;
        cout << "i = " << i << " j = " << j << endl;
        cout << "after Swap" << endl;
        Swap(i,j);
        cout << "i = " << i << " j = " << j << endl;

        double x = 1.1;
        double y = 9.9;
        cout << "x = " << x << " y = " << x << endl;
        cout << "after Swap" << endl;
        Swap(x,y);
        cout << "x = " << x << " y = " << y << endl;

        int d1[NUM] = {0,1,2,1,2,0,1,9};
        int d2[NUM] = {0,1,0,1,1,9,7,0};
        Swap(d1,d2,NUM);
        Show(d1);
        Show(d2);

3 显示具体化

假设定义了如下结构

#include <iostream>
struct job
{
        char name[40];
        double salary;
        int floor;
}                        

假设希望交换两个这种结构的内容,原来的模板使用下面的代码来完成交换

temp = a;
a = b;
b = temp; 

c++允许一个结构赋值给另一个结构,因此即使any是一个job结构,上述代码也是适用的。但是,假如我们只想交换salary和floor成员呢?则需要使用不同的代码,但是Swap的参数仍然是一致的(两个job结构体的引用),因此无法使用模板重载的方法来提供其他的代码

不过,可以 提供一个具体化函数定义---称为显式具体化。

  • 对于给定的函数名,可以有非模板函数,模板函数和显式具体化函数以及他们重载的版本
  • 显式具体化的函数的原型和定义应以template<>打头,并通过名称来指出类型
  • 具体化将覆盖常规模板,而非模板函数将覆盖具体化和常规模板,也就是说,编译器会优先寻找非模板函数,在寻找显式具体化函数,最后是常规模板函数

对于三种函数的形式,形如

/*非模板函数*/
void Swap(job & a, job &b);  

/*普通模板*/
template <class any>
void Swap(any &a, any &b);

/*显式具体化*/
template <> void Swap<job> (job &a, job &b);

 

显式具体化中Swap<job>中的job是可以省略的

来看一个例子

#include <iostream>                                                                                                                                              
struct job 
{
        char name[40];
        double salary;
        int floor;
};
template <class any>
void Swap(any &a, any &b) 
{
        any tmp;
        tmp = a;
        a = b;
        b = tmp;
}
template <class any>
void Swap(any a[], any b[], int n)
{
        any temp;
        int i;
        for(i = 0; i < n; i++) {
                temp = a[i];
                a[i] = b[i];
                b[i] = temp;
        }
}
template <> void Swap (job &a, job &b)
{
        double t1 = a.salary;
        a.salary = b.salary;
        b.salary = t1;
        int t2 = a.floor;
        a.floor = b.floor;
 }

int main()
{
        using namespace std;
        int i = 10;
        int j = 20;
        cout << "i = " << i << " j = " << j << endl;
        cout <<"after swap" << endl;
        swap(i,j);
        cout << "i = " << i << " j = " << j << endl;

        job xiaoming = {"liming",10000,2};
        job xiaohong = {"lihong",20000,3};

        cout << xiaoming.name << "salary is " << xiaoming.salary << " on floor " << xiaoming.floor << endl;
        cout << xiaohong.name << "salary is " << xiaohong.salary << " on floor " << xiaohong.floor << endl;
        cout <<"after swap" << endl;
        Swap(xiaoming,xiaohong);
        cout << xiaoming.name << "salary is " << xiaoming.salary << " on floor " << xiaoming.floor << endl;
        cout << xiaohong.name << "salary is " << xiaohong.salary << " on floor " << xiaohong.floor << endl;

        return 0;                                                                                                                                                
}

执行结果

要注意的一点是模板的显式具体化不能单独存在,而是作为模板的补充存在的。并且显式化的声明和定义要放在普通模板之后,它是普通模板的补充

4 实例化和具体化

        为进一步了解模板,必须理解实例化和具体化。

        模板本身不会生成函数定义,只有在函数调用时,编译器才会根据模板生成函数定义。这种方式是隐式实例化。还有显式实例化,也就是可以直接命令编译器创建特定的实例,句法是,声明所需的种类——用<>符号表示类型,并在声明前加上关键字template

template coid Swap<int>(int,int)

编译器看到上述声明后,将使用Swap()模板生成一个使用int类型的实例。

现在我们将上述几种情况总结一下

...
template <class any>
void Swap (any &, any &); //通用模板
template <> void Swap<job> (job &, job &); //具体化
int main()
{
    template void swap<char> (char &, char &); //显式实例化

    short a,b;
    Swap(a,b); //使用通用模板,隐式实例化

    job n,m;
    Swap(n,m); //使用具体化

    char i,j;
    Swap(i,j); //使用显式实例化
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值