IT狗工作室C语言&嵌入式

[笔记No.1]C++模板攻略-函数模板

2019-12-27  本文已影响0人  铁甲万能狗

前言

《C++ Templates Complete Guide 2nd Edition》涵盖了C++11,C++14和C++17的所有内容。如果你的C++编程修为想更进一步的话,那么这本书是一本非常好的C++编程进阶书籍,阅读这本书的必须具备了C++中等程度以后的基础,读这本书没什么压力。虽然大部分写C++模板技术的技术文章,没有显式地说明,但我认为专攻C++方向,这是学习路线图中一个比较正确的顺序(要点不全).请确保你深入学习模板之前,最起码要确保有如下C++基础。

我一直深受一句话的影响:“C的灵魂是指针,而后继者C++的核心技术就是模板”,因为C++ 标准库本身各种类库和函数API就大规模地使用了模板技术。

这本书绝对是一本良心的书籍,因为从第一版到第二版作者一直坚持不懈地跟随C++新版的步伐,扩充了有关模板技术的新内容。网上那些所谓的阿猫啊狗“原创”的C++模板技术培训班用到教学素材要么来自这本书的第一版或者第二版。因此,我认为要完全透彻掌握C++模板技术,一定要看这本书的英文原版。

如果你的C++编程技能偏向于为其他高层语言例如Python/Cython/Java等写更底层类库,函数API和专攻算法实现,那么这本书是必读的。就我目前的程度而言,要实现用C++现实各种常用的算法,我认为完成Part 1的模板知识已经够用了,如果需要升华到融合目前主流的设计模式并设计出自己独有的编程模式的,那么可能要将这本书啃完。这本书分三卷《Part1》,《Part2》和《Part3》。我前期的读书随笔,仅会完成Part1。


封面

罗嗦的话说到这里,我们进入正题

攻略要点

基本术语入门,按图说话,正入下文。

下面的函数模板定义了一个关于max的函数族,其中T是模板的模板参数(Template Parameter)可以表示一个具体的数据类型,例如int,double,std::string等等...

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

其中的typename关键字来定义模板参数,也可以使用class关键字替换

调用示例

int main()
{
    auto r1=::max(2.3,44.44);
    auto r2=::max(77.23,52);
    auto r3=::max("Hello-World!!","IT-dog");

    
    std::cout<<"r1:"<<r1<<std::endl;
    std::cout<<"r2:"<<r2<<std::endl;
    std::cout<<"r3:"<<r3<<std::endl;
    return 0;
}

上面的调用示例,max函数模板没有被编译成可以处理任何类型的单一实体。 相反,对于使用该模板的每种类型,都会从模板生成不同的实体。 也就是编译后会有三个关于max函数的重载版本。因此,将针对这三种类型中的每种类型编译max()。 例如,

返回r1对应的max函数体

double max(double a,double b);

返回r2对应的函数体是

double max(double a,int b);

返回r3对应的max函数签名

std::string max(std::string a,std::string b);

那么上面关于函数max的重载集中,不同的重载版本的参数类型,C++的模板系统是如下推导的呢?

模板参数的推导过程

当我们为某些参数调用函数模板(例如max())时,模板参数的最终类型由我们传递的参数的类型确定的。 如果我们将两个int传递给参数类型T,则C ++编译器就得知T必须为int。

请注意,在类型推导过程中,自动类型转换受到限制:

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

int c=72;
const int b=42;
max(b,c);   //T会被推导为int
max(c,c);  //T会被推导为int

int i=74;   
int &r=i;  
max(i,b);  //T会被推导为 int类型

int arr[3];
foo(&r,arr); //T会被推导为int*

然而下面向模板参数传递的第一个参数是int类型,第二个参数是double类型,但max函数模板的模板参数返回类型只定义了T,C++编译器要么匹配int max(int,int),要么匹配double max(double,double)

max(4,7.2);

避免这种编译时的的错误,我们在调用代码中会最好显式传入模板参数

max<double>(4,7.2);

max(static_cast<double>(4),7.2)

这样C++编译器可以推导为 模板参数T为double,但后一种,我个人是不太喜欢这样书写代码的。

默认参数的类型推导

类型推导不使用与默认调用参数

template<typename T>
void foo(T=""){}

int main()
{
    foo();
    return 0;
}
2019-12-27 19-18-14屏幕截图.png
为了支持这种情况,我们必须为模板参数声明一个默认的参数类型,
template<typename T=std::string>
void foo(T=""){}

int main()
{
    foo();
    return 0;
}

下面的示例,C++编译器能够推导的模板参数T为std::string类型,即生成的void foo(std::string)

多个模板参数

上面的max模板参数,我们可以为多个不同模板参数,下面的例子,我们我们在template的模板参数列表,定义了两个模板参数,分别是T1,T2表明它们告知编译器可以匹配两个不同类型的参数,并且max函数模板的返回类型为T1,表明匹配时,返回类型取决于在匹配时第一个模板参数推导的类型

template<typename T1,typename T2>
T1 max(T1 x,T2 y){
      return x>=y?x:y;
}

auto r1=::max(4,7.2);
auto r2=::max(24.5,8);

上面例子中r1对应的max函数重载版本为int max(int,double),因为在调用函数中,向max的模板函数传入的第一个参数是4,是一个int类型,而该类型决定了模板函数的返回的类型是int。

同理,r2对应的max函数重载版本为double max(double,int);

有时这样的情况,不是我们所期望的。因此有集中不同的解决方法。

1.3.1返回类型的模板参数

template<typename T1,typename T2,typename RT>
RT max(T1 a,T1 b){ ....}

int main(void){
    ::max<int,double,double>(44,63.2);
}

我个人不喜欢这种方式,可以再次改进一下上面的例子

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

int main(void){
      ::max<double>(44,63.2);
}

上面的例子,仅显式指定返回类型为double,剩下的模板参数由传入参数的类型来推导。

1.3.2推导返回类型

#include <type_trains>

template<typename T1,typename T2>
auto max(T1 a,T2 b)-> typename std::decay<decltype(a>b?a:b)>::type{
    return a>b?a:b;
}
int main(void){
    auto r1=::max(4,7.2);
    auto r2=::max(22.1,8.3);
    
    std::cout<<"r1:"<<r1<<std::endl;
    std::cout<<"r2:"<<r2<<std::endl;
}

实际上,对返回类型使用auto而不使用相应的尾随返回类型(将在末尾添加->)表
示必须从函数体内的return语句推导出实际的返回类型.这是使用类型特征std::decay<>,它以成员类返回结果类型。由于成员类也是一种类型,因此它必须用typename限定表达式才能访问。

1.3.3 返回值是一般类型

这是C++11之后,标准哭提供的一种方式std::common_type_t<>

#include <type_trains>

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

首先,我个人是不喜欢这种方式,为何我简单方便的auto关键字不用,我还要敲打那么多代码~!,而且作者也认为这种方式对后期维护会带来一些麻烦的?因此我这里就不往下说了。请查看原书的48页的内容。

1.4 默认模板参数

(略)我个人同样的返回值推导复杂化,没必要使用(原书49-51页的内容)。

重载模板函数

后面会继续更新....

上一篇下一篇

猜你喜欢

热点阅读