C++函数模版

概念

函数模版是通用的函数描述,也就是说,它们使用泛型来定义函数,其中的泛型可以用具体的类型来进行代替,通过该类型作为参数传递给该模版,可以使编译器生成该类型的函数

例如:

有一个交换两个int类型的函数,假如要更改为交换两个double类型的函数,一种方法是复制原来的代码,并且都double替换掉所有的int,但是这种修改会浪费时间,并且容易出错,但C++的函数模版功能能够自动完成这个过程

函数模版的定义例子

template <typename, AnyType >

void swap(AnyType &a , AnyType &b)

{

        函数体;

}

第一行指出,要建立一个模版,并将类型命名为AnyType, 关键字template和typename是必须的除非可以使用关键字class来代替typename,类型名AnyType可以任意选择

例子:

#include<iostream>

using namespace std;

template <typename T >
void swapi(T &a , T &b);
int main(void)
{
        int a = 10;
        int b = 20;
        cout<<"a =  "<<a<<" b = "<<b<<endl;
        swapi(a,b);
        cout<<"a =  "<<a<<" b = "<<b<<endl;
        double x= 2.5, y = 3.5;
        swapi(x,y);
        cout<<"x =  "<<x<<" y = "<<y<<endl;

}

template <typename T>
void swapi(T &a , T &b)
{
        T tem = a;
        a = b;
        b = tem;

}

在本例中将类型命名为T,注意,在运行此程序时,调用了两次swapi函数,编译器会生成两个swapi函数,一个是int类型的,一个是double类型的,即最终也会生成两个独立的函数,使用函数模版使得编译器能够生成多个函数,使得生成函数更简单可靠

我们通常会将函数放在头文件中,并在使用模版的文件中包含头文件

重载的模版

当我们需要对多个不同类型的数据使用同一种算法时,可以使用模版,但是并不是所有的类型都使用相同的算法,为了解决这种问题,就可以像函数重载那样使用重载模版进行定义,注意,被重载的模版的特征标(参数)必须不同,例如

原来的模版:        (T &, T  &);

新模版       :        (T a[], T b[] &,  n); //交换两个等长数组的值

两种的特征标不同

程序运行调用时,编译器根据传入参数的不同,选择使用不同的函数模版

函数模版的局限性

函数模版不一定能够处理所有传入的形参,按照上例实现两个数的交换,如果传入的参数是数组名,那么就无法进行处理,如果传入的是结构体,而我们只想交换结构体中的某些成员,而不相进行全部的交换,上述的模版也无法实现

解决方法:显示具体化的函数模版

template <> 函数返回值 函数名<指定的类型>(形参列表)

 连接上例

typedef struct job
{
    char *s;
    int i;
    double b;
}job;

//这条语句告诉编译器swapi无法处理job类型的数据
//需要使用特殊的处理,而不是使用原来的函数模版
template <> void swapi<job>(job &j1, job &j2)
{
    //只进行部分的交换
    double t = j1.b;
    j1.b = j2.b;
    j2.b = t;

    int c = j1.i;
    j1.i = j2.i;
    j2.i = c;
}

注意:

对于给定的函数,可以有非模版函数,模版函数,显示具体化模版函数以及它们的重载版本

显示具体化的原型和定义应该以template<>开头,并通过名称来指出类型

具体化优先于常规模版,而非模版函数优先于具体化和常规模版

实例化和具体化

我们在代码中包含函数模版本身并不会生成函数的定义,它只是一个用于生成函数定义的方案,编译器使用模版为特定的类型生成函数定义时,得到的是模版的实例,即函数模版并不是函数的定义,只有在真正的传入参数的时候,模版才会转化为函数的定义,这被称为实例化,编译器自动进行的称为隐式的实例化,现在,可以进行显示的实例化,例如

语法为声明所需的种类,用<>符号来指定类型,并在声明前加上关键字template

如下所示:

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

这意味着可以直接命令编译器创建特定的实例

注意,显示具体化的template后是加有<>符号的,而实例化并没有

当我们的模版函数需要处理一种特殊的数据类型时,使用显示具体化,进行特殊的处理

而显示实例化是无论该函数是否被调用,我们都要为当前的模本函数生成一个函数的定义,如果没有进行显示实例化,那么什么时候调用什么时候才会将模版函数的定义,实例化主要用于库文件的编写

自己选择调用的函数

函数名<>(实参);

//此时<>告诉编译器去调用模版函数

函数名<数据类型>(实参);

//此时<数据类型>告诉编译器要将实参转化为指定的数据类型,再去调用模版函数

关键字decltype(C++11)

在处理函数模版时,可能不知道给某个变量具体的类型

template <typename T1, typename T2>

void ft(T1 x, T2 y)

{

        return ps = x+y;

}

此时不知道ps的类型到底是什么,是T1类型还是T2类型的,在这种情况下,没有办法声明ps的类型,而C++11的关键字decltype提供了解决的方法,例如

int x;

decltype (x) y;

此时让y的类型与x的类型一致,并且,给ps提供的参数也可以是表达式

decltype (x+y) ps = x + y;

编译器确定类型的核对表

decltype(表达式) var

1.如果表达式是一个没有括号括起来的标识符,则var的类型与标识符的类型相同,例如

int a;

decltype (a) b; b也为int类型

2.如果表达式是一个函数的调用,则var的类型与函数的返回值的类型相同

注意:此时并不会实际的调用函数,编译器通过查看函数的原型来获取返回值

3.如果表达式是一个左值,并且是由括号()括起来的,那么var为指向其类型的引用,例如

int a = 10;

decltype((a)) b;

decltype(a) c;

此时b是a类型的引用,而c为int类型

4.如果前面的条件都不满足,则var的类型与表达式的类型一致

C++11后置返回类型

有一个问题decltype也无法解决,即

template <typename T1, typename T2>

类型? ft(T1 x, T2 y)

{

        return x+y;

}

此时返回值的类型不确定

使用auto关键字

auto h(int a, double b) -> double

此时是后置返回类型,这样就可以解决上述的问题

template <typename T1, typename T2>

auto ft(T1 x, T2 y) -> decltype(x+y)

{

        return x+y;

}

此时指定函数的返回值被指定为x+y类型

  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值