STL库-函数模板知识总结

1.函数模板

1.1 函数模板的定义

注意:

  • c++中<>里面是类型形参列表,传入的是类型名称,不是变量

函数模板的定义形式:

//c++中<>里面是类型形参列表,传入的是类型名称,不是变量
//template、vector
//注意:可以使用任何标识符作为类型形参的名称,但是使用"T"已经称为一种惯例
//简单来说,T可以代替任何类型
//注意:class可以和typename相互替换
template<class 类型形参1, class 类型形参2, ...>
返回值类型 函数模板名(调用形参1, 调用形参2, ...){
   ...
}

//举例
template<class T>
T Max(T x,T y){
    return x>y? x:y;
}

1.2 函数模板的使用(实例化)

注意:

  • 使用函数模板,要先对函数模板进行实例化

形式

  • 用具体数据类型替换函数模板类型形参
函数模板名<类型实参1, 类型实参2, ...>(调用实参1, 调用实参2,...);

//举例
Max<int>(123,456);
//注意:使用函数模板就包含了函数模板的实例化,因此不需要再去写额外的代码去实例化函数模板
//直接当做函数调用来使用就行了。

1.3 函数模板感性认知

编译器不是把函数模板编译成一个可以处理任何数据的单一实体,
而是,在实例化模板函数时,根据类型实参,从模板函数中产生一个真正的函数实体。

也就是说,编译器看见实例化函数模板的代码的时候,编译器就会依照我们定义的函数模板的格式,帮我们生成一个函数实体。

所以,最后编译完成以后,不会保存函数模板的任何信息,只会保存函数模板帮我们生成的函数。
函数模板本质就是一个编译器帮我们生成函数的_依据。**

编译器在实例化模板的时候会干两件事:

  • 依据我们定义的模板,生成实体化函数。(函数重载)
  • 将我们实例化模板的地方替换成生成的实体函数的调用。

(具体代码实现看1.4下面的代码)

1.4 实例化函数模板的条件

  • 原则上,可以用任何类型来实例化函数模板,包括基本类型和类类型
  • 前提:这个类型必须支持函数模板中所要执行的操作。
#ifndef FUNCTMPL_H_INCLUDED
#define FUNCTMPL_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;

class Integer{
public:
    Integer(int i):m_i(i){}
    //注意,这个类中没有重载'>'操作符,所以不支持>运算。
    //重载>以后,编译器依据函数模板生成的函数就能正确执行了
    bool operator>(Integer const& that)const{
        return m_i > that.m_i;
    }
private:
    int m_i;
};

//定义函数模板
template<class T>
T Max(T x, T y)
{
    return x > y ? x : y;
}

void TemplateExample()
{

    Integer ix=100, iy=200;
    Max<Integer>(ix, iy);
    //编译器生成的函数:
    //Integer Max(Integer x, Integer y){return x > y? x : y;}
    //如果Integer中没有重载>运算符编译器生成的第32行的代码就会运行错误,
    //也就是说,这个类型不支持>操作。所以无法正常实例化。
    int nx=10, ny=20;
    cout << Max<int>(nx, ny) << endl;
    //int Max(int x, int y){...}//step1:编译器帮我们生成的函数
    //      Max(nx, ny)         //step2:将Max<int>(nx, ny)替换成Max(nx,ny)
    double dx=12.3, dy=45.6;
    cout << Max<double>(dx,dy) << endl;
    //double Max(double x, double y){...}//编译器帮我们生成的函数
    //      Max(dx, dy)
    string sx="world",sy="hello";
    cout << Max<string>(sx, sy) << endl;
}



#endif // FUNCTMPL_H_INCLUDED

1.5 二次编译

  • 编译器(g++)对函数模板都会进行两次编译

1.5.1 第一次编译:发生在实例化函数模板之前(产生真正函数实体之前)

  • 只检查模板本身内部代码,查看基本语法词法是否书写正确
    • 函数模板内部出现的所有标识符是否都有声明
    • 对于已知类型的调用,查看是否合理有效
    • 对于未知类型的调用,编译器认为都合理
      • 因为编译器也不知道你要给这个未知类型传谁,这个位置类型到底能不能调用这个方法,编译器不知道,但是,编译器默认写代码的人是知道的,所以就不给报错
#ifndef TWOCOMPILE_H_INCLUDED
#define TWOCOMPILE_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
class A{
public:
    A(){}
    void func() {
        cout << "A::func()" << endl;
    }
};

template<class T>void foo()
{
    //1、检查函数模板内部出现的所有标识符是否都有声明
    //abcde;

    //2、对于已知类型的调用,查看是否合理有效
    A a;
    a.func;//已知类型的调用

    //3.对于未知类型的调用,编译器认为都合理
    T t;//这里T就是未知类型,t是未知类型的对象
    t.fdjioagoijagd();//对未知类型的调用
}

#endif // TWOCOMPILE_H_INCLUDED

1.5.2 第二次编译:发生在实例化函数模板之后(产生真正的函数体以后)

第二次编译发生在实例化函数模板以后(产生真正的函数体以后),这时候其实就不存在未知类型了,
所以,要结合所使用的类型实参,再次检查模板代码,查看所有调用是否真的均有效。

#ifndef TWOCOMPILE_H_INCLUDED
#define TWOCOMPILE_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
class A{
public:
    A(){}
    void func() {
        cout << "A::func()" << endl;
    }
};

template<class T>void foo()
{
    //1、检查函数模板内部出现的所有标识符是否都有声明
    //abcde;

    //2、对于已知类型的调用,查看是否合理有效
    A a;
    a.func();//已知类型的调用

    //3.对于未知类型的调用,编译器认为都合理
    T t;//这里T就是未知类型,t是未知类型的对象
    t.fdjioagoijagd();//对未知类型的调用

}

void test() {
    foo<A>();
    //编译器第二次检查:会进行报错,因为A类中没有fdjioagoijagd()函数
}


#endif // TWOCOMPILE_H_INCLUDED

第二次编译器检查报错结果:

在这里插入图片描述

1.6 隐式推断类型实参

**注意**

  • 在c++中,所说的隐式,就是指这个功能是由编译器完成的
  • 在c++中,所说的显示,就是指这个功能是由程序猿完成的

1.6.1 隐式推断类型实参应用和好处

如果函数模板的调用形参(的类型)和类型形参一样

template<class T>T Max(T x, T y){...}

那么,在实例化函数模板的时候,就算没有显示指明函数模板的类型实参,编译器也会根据调用实参类型推断出正确的类型实参。

Max(123, 456);//我们这么写

Max<int>(123,456);//编译器这么推断

这样写的好处(让编译器隐式推断的好处)

  • 在调用函数模板的时候,可以获得和普通函数一样的书写形式(一样的语法表现形式)
#ifndef DEDUCTION_H_INCLUDED
#define DEDUCTION_H_INCLUDED
#include <iostream>
#include <string>

using namespace std;

 //定义函数模板
template<class T>
T Max(T x, T y)
{
    return x > y ? x : y;
}

void TemplateExample()
{

    int nx=10, ny=20;
    cout << Max(nx, ny) << endl;
    //      Max<>(nx, ny) --> Max<int>(nx, ny)//编译器会自己推断类型实参
    double dx=12.3, dy=45.6;
    cout << Max(dx,dy) << endl;
    //      Max<double>(dx, dy)
    string sx="world",sy="hello";
    cout << Max(sx, sy) << endl;
    //      Max<string>(sx, sy)
}

#endif // DEDUCTION_H_INCLUDED

1.6.2注意:有三种情况,编译器无法进行隐式推断

  1. 调用参数和类型参数不完全相关
template<class T, class D>T Max(T x, T y){ }
  1. 隐式推断不支持隐式类型转换(隐式推断和类型转化不能同时进行)
template<class T> T Max(T x, T y){...}
Max(123, 45.6);
  1. 返回值类型也不支持隐式推断
#ifndef DEDUCTION_H_INCLUDED
#define DEDUCTION_H_INCLUDED
#include <iostream>
#include <string>

using namespace std;

 //定义函数模板
template<class T>
T Max(T x, T y)
{
    return x > y ? x : y;
}

template<class T, class D>void Func(D x){

}

template<class R, class T>R Hum(T x){
    R r;
    return r;
}

void TemplateExample()
{
    int nx=10, ny=20;
    cout << Max(nx, ny) << endl;
    //      Max<>(nx, ny) --> Max<int>(nx, ny)//编译器会自己推断类型实参
    double dx=12.3, dy=45.6;
    cout << Max(dx,dy) << endl;
    //      Max<double>(dx, dy)
    string sx="world",sy="hello";
    cout << Max(sx, sy) << endl;
    //      Max<string>(sx, sy)

    Func<string>(nx);
    //Func(nx);//1、类型实参列表不完全和调用实参列表相匹配,报错
    //只要给了一个类型形参,另一个就能推断出来了

    Max(nx, (int)dy);//强制类型转换,显示转换,不报错
    //Max(nx, dy);//2、让编译器,做类型推断的同时隐式转换,报错

    Hum<double>(nx);//给定R的类型,运行成功
    //Hum(nx);//3、没有给定返回值R的类型,编译器无法推断,报错

}

#endif // DEDUCTION_H_INCLUDED

1.7 函数模板的重载

普通函数和可以实例化该函数的函数模板构成重载关系

  1. 在数据类型匹配度一样,编译器选择普通函数,不选择函数模板。
  2. 若,函数模板可以产生具有更好数据类型匹配度的实例,那么选择函数模板。
  3. 函数模板的实例化不支持隐式类型转换,但是普通函数支持。(1.6.2中的知识)
  4. 可以在实例化的时候用<>强行通知编译器选择函数模板。
#ifndef OVERLOAD_H_INCLUDED
#define OVERLOAD_H_INCLUDED
#include <iostream>
#include <string>

using namespace std;

void Max(int x, int y){
    cout << "调用普通函数:Max(int, int)" <<endl;
}

template<class T>void Max(T x, T y){
    //这个函数模板和上面的函数就是重载关系
    //因为,我用这个函数模板有可能能实例化出一个
    //和上面一样的函数
    cout << "调用函数模板:Max(T, T)" << endl;
}

void OverLoadTest(){
    int nx=10, ny=20;
    cout << "1.数据类型匹配度一样,选择普通函数" <<endl;
    Max(nx, ny);
    cout << endl;
    double dx = 34.2, dy = 45.6;
    cout << "2.函数模板产生的实例函数能更好匹配传入的数据类型,选函数模板" << endl;
    Max(dx, dy);
    cout << endl;
    cout << "3.函数模板不支持隐式类型转换,使用普通函数" << endl;
    Max(nx, dy);//有类型转换,选择普通函数,不选函数模板
    cout << endl;
    cout << "4.使用<>强制使用函数模板,就算数据匹配类型一样" << endl;
    Max<>(nx, ny);//加上<>强制使用函数模板
    cout << endl;
}

#endif // OVERLOAD_H_INCLUDED

代码运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辛伯达岛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值