c++ 函数模板详细介绍

在阅读dds源码时,看到了函数模板的用法,时间比较久了,优点模糊了,这里重新进行总结一下。至于什么是函数模板,在该专栏的其它文章有进行介绍。这里不进行介绍,本文仅对如何使用函数的各种方法进行介绍。下面将给出一个最简单的模板例子:

template<typename T>
T max(T a, T b)
{
    return a < b ? b:a;
}

 下面是一个使用函数模板的例子:

double value =max(7.7, 8.8);  //函数模板使用例子

上面的例子大家都能看明白,即求两个函数中的较大值,这里展开两个概念:

  • 模板参数,template尖括号中的“typename T”就是模板参数
  • 调用参数,实际使用时,即函数模板实例化后的参数就是调用参数,就是上例中的7.7和8.8。

上面的例子很简单,但是大家有没有思考过一个问题,上面的例子是一个模板参数,并且调用参数和函数返回类型都是同一类型,那如果是两个或者多个模板参数呢?返回值类型不需要经过类型推导或者需要通过模板参数指定返回类型的情况,函数模板应该如何使用呢?下面将一一进行介绍。

函数模板-两个模板参数,返回值类型为调用参数类型的一个

先声明一个函数模板:

template<typename T1, typename T2>
T1 max(T1 a, T2 b)
{
    return a > b ? a: b;
}

看到上面的声明,可以看到max函数模板包含两个调用参数,并且属于不同的类型,返回值类型是是第一个参数的类型,这种使用方法相信大家也能够看明白,参数和返回值类型清清楚楚,明明白白。下面给出使用方法:

方法一:

double value = max(6, 7.7);//第一个参数为整型,第二个参数浮点型 返回值类型为浮点型

方法二:

double value = max<double>(6, 7.7);//第一个参数为整型,第二个参数浮点型 返回值类型为浮点型

 思考上面的两种方法,第一种是未声明参数类型,需要通过编译器的类型推导功能;而第二种方法则是显式的声明参数类型。

函数模板-两个模板参数,返回值类型与调用参数类型无关

话不多说,先给出声明

template<typename T1, typename T2, typename RT>
RT max(T1 a, T2 b)
{
    return  a > b ? a : b;
}

看出区别来了吧,返回值类型是模板参数中的一个,其实也是相当于显式指定的,这里提到这种方式,是因为我们如何使用,请仔细的想想看,这个声明我们应该如何使用呢?使用方法如下:

double value = max<int,double,double>(7, 8.8);

思考上面为什么一定要显式的声明参数呢?能不能通过编译器的类型推导功能来实现自动推导呢?答案是否定的。因为这里的RT类型放在最后,这个RT编译器无法推导,所以必须要显式的声明出来,通过查阅资料得知,如果模板参数与调用参数数量不一致,那么在编译器无法推导的类型前的所有模板参数都需要显式的声明出来,上面的例子中RT类型放在了最后一个,并且模板参数3个,调用参数只有两个,而且RT类型不能通过类型推导的方式导出类型,所以要将RT类型前的所有类型都显式的声明出来。

那么思考另外一个问题,如果调整下模板参数的顺序,该如何使用函数模板呢?修改后例子如下:

template<typename RT, typename T1, typename T2>
RT max(T1 a, T2 b)
{
    return  a > b ? a : b;
}

根据上面的分析,使用方法如下:

double value = max<double>(7, 8.8);

可以看到由于RT类型无法推导,并且位于模板参数的第一个,其前面没有参数类型,故仅显示的声明RT的类型即可,后面两个参数的类型由编译器推导得出。我在DDS的源码中看到的就是此种使用方法,由于对这种规则记忆变得模糊,故在这里进行温故一番。

总结:

  • 在使用函数模板时,如果不清楚相关的规则,将模板中的类型全部显式的声明出来,肯定不会产生错误。
  • 如果调用参数的类型都可以类型推导,可以不声明任何参数类型
  • 如果模板参数中存在不能通过类型推导判断模板类型的,那么必须要将该模板参数前面的模板参数包括该模板参数显式的声明出来。

拓展

delcltype:这是一个关键字,使用该关键字又叫尾置返回类型,该关键字的作用是通过该关键字指定的表达式推导函数模板的返回值类型,,这个关键字一般在c++11下应用的比较多,在c++14下对类型推导进行了改进优化,一般不使用该关键字。一般用法如下:

tmplate<typename T1, typename T2>
auto max(T1 a, T2 b)->decltype(a>b?a:b)
{
    return a>b?a:b;
}

看到区别了吗?首先返回值采用了auto,其次使用了关键字decltype。那么使用decltype和不使用type的区别在哪里呢?

答:如果不使用decltype关键字,要么显式的指定返回值类型,但是这个有可能造成精度丢失,不符合用户预期,例如返回值类型显式指定为int,然后调用max(5.5, 4),很明显,返回值是5,丢失了0.5。某些情况下就会与预期不一致;要么就是有编译器根据return a>b?a:b;这个语句进行类型推导,也就是说这种情况下返回值的类型与函数体的实现有关;而使用decltype关键字,则不依赖函数体的实现,仅通过声明就可以推导出返回值类型。

c++14下的对c++11的函数模板类型推导做了改进,不需要使用decltype关键字,仅使用auto关键字即可:

tmplate<typename T1, typename T2>
auto max(T1 a, T2 b)
{
    return a>b?a:b;
}

std::common_type_t<T1, T2>      //从C++14起

common_type_t该关键字是c++14引入的,这个关键字的作用是什么呢?按照我的理解找到是“共同类型”,例如std::common_type_t<T1, T2>,从T1和T2两个类型的找到两个类型的共同类型,并作为返回值,例如std::common_type_t<int,double>,这个共同类型就是double;如果是一个类呢?例如std::common_type_t<Derived,Base>,这个共同类型是Base,因为Base是Derived的基类。这下大致明白这个关键字的意思了吧。

c++11下这个关键字的写法:

typename std::common_type<T1, T2>::type      //从C++11起

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值