• 3.3 C++高级编程_函数模板_引入


    在C++中,模板分为两种:

    1. 函数模板;
    2. 类模板;

    本节主要讲解函数模板。

    所谓模板,就是为了节省代码,对于那些相似的代码,我们可以重复使用

    引入函数模板

    假设我们要写一个比较两个 int 类型变量谁大的函数,那么可以这样写:

    1. int &my_max(int &a, int &b)
    2. {
    3. return ((a < b) ? b : a);
    4. }

    比较两个 double 型:

    1. double &my_max(double &a, double &b)
    2. {
    3. return ((a < b) ? b : a);
    4. }

    两个 float 型:

    1. float &my_max(float &a, float &b)
    2. {
    3. return ((a < b) ? b : a);
    4. }

    可以看到,后面两个double和float的比较函数,其实都是对第一个函数的重载。

    这些函数这么相似,是否有什么办法可以简化代码,不用定义这么多函数呢?

    答:可以的,使用函数模板

    函数模板的格式如下:

    1. template<类型参数表>
    2. 返回值 函数名(数据参数表)
    3. {
    4. 函数模板定义体
    5. }

    代码如下,将输入参数的类型和返回值的类型都用T代替,同时使用__PRETTY_FUNCTION__宏,增加一些调试信息:

    1. template
    2. T &my_max(T &a, T &b)
    3. {
    4. cout << __PRETTY_FUNCTION__ << endl;
    5. return ((a < b) ? b : a);
    6. }

    修改main函数。

    1. int main(int argc, char **argv)
    2. {
    3. int ia = 1, ib = 2;
    4. cout << my_max(ia, ib) << endl;
    5. return 0;
    6. }

    编译测试,可以看到,调用了模板定义的my_max函数,并且成功返回了对比的两数中较大的值:

    函数模板的内部机制

    那么,函数模板的内部机制是怎么样呢

    先来看一下刚刚生成的可执行文件max,可以看到大小是9440B。

    修改一下main函数,增加double,float型的比较。

    1. int main(int argc, char **argv)
    2. {
    3. int ia = 1, ib = 2;
    4. double da = 1, db = 2;
    5. float fa = 1, fb = 2;
    6. cout << my_max(ia, ib) << endl;
    7. cout << my_max(da, db) << endl;
    8. cout << my_max(fa, fb) << endl;
    9. return 0;
    10. }

     重新编译测试:

    此时,再查看一下max文件的大小:

    文件大小从9440B增加到了13880B,增加了 13880 - 9440 = 4440B,只是增加了几个变量和函数调用,为什么文件大小会有这么大的变化呢

    这就和函数模板的内部机制有关了。

    编译器在编译代码时,实际上不会编译函数模板(函数模板不是函数,而是编译指令),而是根据代码的需要,选择使用哪个函数模板生成对应的函数。

    当main函数中只需要比较 int 型的my_max函数时,编译器根据函数模板在代码中生成 int 型的my_max函数;同理,需要double型和float型的比较函数时,也会对应生成这两个类型的比较函数。

    也就是说,表面上看是多定义了几个变量,加了几个函数调用,实际上编译器编译时会多出两个函数定义

    参数的推导过程

    之前定义的函数模板中,要求传入的两个参数a和b的类型相同,都为T。

    1. template
    2. T &my_max(T &a, T &b)
    3. {
    4. cout << __PRETTY_FUNCTION__ << endl;
    5. return ((a < b) ? b : a);
    6. }

    那么,如果传入的参数类型不同会怎么样?

    将b的类型修改为const int,a的类型为int。

    1. int main(int argc, char **argv)
    2. {
    3. int ia = 1;
    4. const int ib = 2;
    5. cout << my_max(ia, ib) << endl;
    6. return 0;
    7. }

    此时编译会有报错,报错原因编译器找不到声明为my_max(int&, const int&)的函数声明,也就是无法从函数模板中生成对应的函数。

     将a的类型也改为const int。

    1. int main(int argc, char **argv)
    2. {
    3. const int ia = 1;
    4. const int ib = 2;
    5. cout << my_max(ia, ib) << endl;
    6. return 0;
    7. }

    此时编译测试可以成功。

    也就是说,函数模板对于模板类型的选择是有严格要求的,必须要与模板定义中保持一致。

    函数模板的隐式类型转换

    对于普通的函数调用,编译器支持很多种隐式类型转换,但是对于函数模板,则只支持两种隐式类型转换:

    1. const转换:函数参数为非const引用/指针,它可以隐式转换为const引用/指针;
    2. 数组的转换:数组可以隐式转换为“指向第1个元素的指针”;
    3. 函数的转换:参数为“函数的名字”时,它隐式的转换为“函数指针”;

    非const转换为const

    修改代码,将函数模板传参和返回值的类型都改为const。

    传入两个int类型的参数。

     

     此时编译执行正常。

    也就是说,非const类型的参数可以转换为const类型的参数。

    相当于权限从可读可写变为了只读。

    注意:const 类型的参数不能转换为非 const 类型,即只读的变量不能转换为可读可写的变量。

    数组的转换

    修改代码,传入两个字符数组。

    编译执行,可以看到T = char[3]。

    增加一个my_max2函数,将传参和返回值的类型从引用改为指针。

     编译执行,可以看到 T 被转换成了char。

    继续修改main函数,将a改为“abc”。

    此时编译会报错,因为T不能同时等于char [4]和char [3]。

     将my_max函数的调用屏蔽,则可以编译成功。

    因为my_max2中T是指针,推导的到的是char类型的指针。

    函数指针的转换

    修改代码,重新创建一个函数模板。

    增加一个测试函数func1。

    然后修改main函数。

    编译测试,发现无论是传入函数名,还是函数的地址,T的类型都是一样的。

    也就是说,传入函数名字时,会隐式的转化为函数的指针。

  • 相关阅读:
    等高度结构的顺序一致性
    Leetcode 381. Insert Delete GetRandom O(1) - Duplicates allowed (数据结构设计好题)
    图像滤波概述
    【深度学习】实验13 使用Dropout抑制过拟合
    【Linux-达梦】A1.数据库部署
    LeetCode50天刷题计划(Day 11—— 最接近的三数之和(8.40-10.00)
    vue考试系统后台管理项目-登录、记住密码功能
    bigemap在林业勘测规划设计行业的一些应用
    SAKO搜索帮助增强(FB02科目搜索帮助)
    86-分布式前端开发
  • 原文地址:https://blog.csdn.net/qq_33141353/article/details/126291976