C++学习笔记3——函数模板

1. 函数模板

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

模板的写法:

template<typename AnyType> // typename可以用class替换,兼容旧的代码
void Swap(AnyType &a, AnyType &b)
{
	AnyType Temp;
	temp = a;
	a = b;
	b = Temp;
}

将同一种算法应用于不同的类型,可用使用模板。
模板并不创建任何函数,只是告诉编译器如何定义函数。
要使用模板的一个特定形式的函数,只需要在程序中调用Swap函数既可。编译器会检查使用的参数类型,并生成相应的函数。
如下所示的代码中使用了函数模板:

#include <iostream>

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


int main()
{
    using namespace std;
    int i = 10;
    int j = 20;
    cout << "i, j = "<<i<<","<<j<<".\n";
    cout << "Using complier-generated int swapper:\n";
    Swap(i, j);
    cout << "Now, i, j = " << i <<", " << j << ".\n";

    double x = 24.5;
    double y = 81.7;
    cout  << "x, y = "<<x<<","<<y<<".\n";
    cout << "Using complier-generated double swapper:\n";
    Swap(x, y);
    cout << "Now, x, y = " << x <<", " << y << ".\n";
    //  cin.get();

    return 0;
}

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

在这里插入图片描述

模板Swap定义了函数生成的方式,传入int类型的参数,编译器就会生成int类型的版本。

2. 重载的模板

可以像重载普通函数那样重载函数模板。仍然是需要特征标不同。如下代码重载了两种不同的Swap函数模板,在使用时传入不同的参数。

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

template <typename T>
void Swap(T *a, T *b, int n);

void Show(int a[]);
const int Lim = 8;


int main()
{
    using namespace std;
    int i = 10;
    int j = 20;
    cout << "i, j = "<<i<<","<<j<<".\n";
    cout << "Using complier-generated int swapper:\n";
    Swap(i, j);
    cout << "Now, i, j = " << i <<", " << j << ".\n";

    int d1[Lim] = {0, 7, 0, 4, 1, 7, 7, 6};
    int d2[Lim] = {0, 7, 2, 0, 1, 9, 6, 9};
    cout <<"Original arrays:\n";
    Show(d1);
    Show(d2);
    Swap(d1, d2, Lim);
    cout <<"Swapped arrays:\n";
    Show(d1);
    Show(d2);
    // cin.get();
    return 0;
}


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

void Show(int a[])
{
    using namespace std;
    cout << a[0] << a[1] <<"/";
    cout << a[2] << a[3] <<"/";
    for(int i = 4;i<Lim;i++)
    {
        cout << a[i];
    }
    cout << endl;
}

在这里插入图片描述
但函数模板具有局限性,其预设了数据类型可以执行的操作。比如对于上面的Swap模板,如果T为数组类型,则a = b就不成立。

对于此种问题,一种方法是可以重载运算符,另外一种就是为特定数据类型提供具体化的模板定义,即显示具体化。

3. 显式具体化

对于给定的函数名,可以有非模板函数,模板函数和显式具体化模板函数,以及它们的重载版本存在。
优先级非模板函数 > 显式具体化模板函数 > 常规模板函数

显示具体化模板的定义方式:

template <> void Swap<job>(job &, job &);

该定义为job类型定义了一种新的模板。

如下的代码使用了显式具体化的模板,定义了对于job结构体的Swap函数模板:

#include <iostream>

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

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

template <> void Swap<job>(job &j1, job &j2);
void Show(job &j);

int main()
{
    using namespace std;
    cout.precision(2);
    cout.setf(ios::fixed, ios::floatfield);
    int i = 10, j = 20;
    cout << "i, j = " << i << "," << j << ".\n";
    cout << "Using complier-generated int swapper:\n";
    Swap(i, j);
    cout << "Now, i, j = " << i <<", " << j << ".\n";

    job sue = {"Susan Yaffee", 73000.60, 7};
    job sidney = {"Sidney Taffee", 78060.72, 9};
    cout << "Before job swapping:\n";
    Show(sue);
    Show(sidney);
    Swap(sue, sidney);
    cout << "After job swapping:\n";
    Show(sue);
    Show(sidney);
    // cin.get();


    return 0;
}


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

template <> void Swap(job &j1, job &j2)
{
    double t1;
    int t2;
    t1 = j1.salary;
    j1.salary = j2.salary;
    j2.salary = t1;

    t2 = j1.floor;
    j1.floor = j2.floor;
    j2.floor = t2;
}

void Show(job &j)
{
    using namespace std;
    cout << "j.name" << ": $" << j.salary
            << " on floor " << j.floor <<endl;
}

在这里插入图片描述

4. 实例化与具体化

实例化和具体化的区别:

实例化:编译器使用模板为特定类型生成函数定义。
具体化:仍然是一个模板,没有生成函数定义,需要在调用时编译器才生成函数定义。

隐式实例化:在调用函数时传入特定类型的参数,如向Swap(T &a, T&b)模板传入int类型的参数,编译器就生成使用int类型的示例。

显示实例化:直接命令编译器创建特定类型的示例,如下:

template void Swap<int>(int, int)

显示具体化声明:如下两种等价声明之一。与显示实例化声明的区别在于template后面有<>符号,显示实例化没有:

template <> void Swap<int> (int &, int &)
template <> void Swap(int &, int &)

5. 函数模板的发展

编写函数模板时,可能出现无法确定声明中使用何种类型的情况:

template<typename T1, typename T2>
void ft(T1 x, T2 y)
{
	……
	?type? xpy = x + y;
	……
}

此种情况下不确定xpy的类型。

C++11新增关键字decltype可以解决此问题。decltype关键字这样使用:

int x;
decltype(x) y; // y与x类型相同

可以给decltype提供表达式,ft可以改写如下:

template<typename T1, typename T2>
void ft(T1 x, T2 y)
{
	……
	decltype(x + y)xpy = x + y;
	……
}

decltype的使用说明:
decltype(expression) var;

(1) 如果expression是一个没有用括号括起来的标识符,则var的类型与标识符的类型相同,包括const等限定符
(2) 如果expression是一个函数调用,则var的类型与函数的返回类型相同
(3) 如果expression是一个左值,则var指向其类型的引用
(4) 如果以上都不满足,则var的类型与expression的类型相同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值