【C++精华铺】8.C++模板初阶

简介: 什么是泛型编程?泛型编程是避免使用某种具体类型而去使用某种通用类型来进行程序编写的方式,依次来复用某段代码而避免大规模功能相似重复冗余的代码。下面的代码如果想用泛型编程该如何实现。马云有次说了这么一句话:“世界是懒人创造的,懒不是傻懒,如果你想少干,就要想出懒的方法。要懒出风格,懒出境界。”C++必然也是有风格有境界的,所以C++中设计了实现了泛型编程。

 目录

1. 泛型编程

2. 函数模板

2.1 函数模板的概念及格式

2.2 函数模板的原理

2.3 模板的实例化

2.4 模板参数的匹配原则

3. 类模板

3.1 类模板格式

3.2 类模板的实例化


1. 泛型编程

       什么是泛型编程?泛型编程是避免使用某种具体类型而去使用某种通用类型来进行程序编写的方式,依次来复用某段代码而避免大规模功能相似重复冗余的代码。下面的代码如果想用泛型编程该如何实现。

int add(int a,int b)
{
  return a + b;
}
double add(double a, int b)
{
  return a + b;
}
double add(int a, double b)
{
  return a + b;
}

image.gif

       马云有次说了这么一句话:“世界是懒人创造的,懒不是傻懒,如果你想少干,就要想出懒的方法。 要懒出风格,懒出境界。”C++必然也是有风格有境界的,所以C++中设计了模板实现了泛型编程。

2. 函数模板

2.1 函数模板的概念及格式

       模板就是一种模具,通过给这个模具中放不同的材料(类型),来获得不同材料的产品,以此来提高我们的工作效率。而函数模板就是某个函数的模具,与类型无关,在使用的时候参数化,在我们给出特定类型就会生成特定类型的版本。

       函数模板的格式:

template

返回值类型 函数名(形参列表)

{

   //函数体

}

        根据这个函数的模板我们就可以实现上述add函数的模板:

template<typename L, typename R,typename RET>
RET add(L l, R r)
{
  return l + r;
}

image.gif

注:typename是用来定义模板参数的关键字,也可以用class代替。

2.2 函数模板的原理

       函数模板本身不是函数,而是一个模具,当我们传入实际类型的时候编译器会根据我们传入的实参类型来推演生成对应类型的函数进行调用。

image.gif编辑

2.3 模板的实例化

       当我们将类型传入模板来生产函数的时候,称之为模板的实例化,模板的实例化分为隐式实例化和显式实例化。

      1. 隐式实例化:隐式实例化就是我们不去指定类型,让编译根据我们传入的实参判断其类型传入模板参数列表进行实例化。如下:

template<typename L, typename R>
int add(L l, R r)
{
  return l + r;
}
int main()
{
  int a = 0, b = 1;
  cout << add(a, b) << endl;  //根据我传入的a和b自动判断类型进行模板实例化。
}

image.gif

       细心的同学发现,这里我将原来的代码中 typename RET 删掉了,原因就是模板不会对函数的返回值类型进行自动判断,而需要我们手动指定(也就是显式实例化)。

       2. 显式实例化:显式实例化就是我们手动向模板传递类型,然后由编译器进行实例化。显式实例化的格式为:

函数名<类型1,类型2,...>(实参列表);

       如:

template<typename L, typename R, typename RET>
RET add(L l, R r)
{
  return l + r;
}
int main()
{
  int a = 0, b = 1;
  cout << add<int,int,int>(a, b) << endl;
}

image.gif

       上述代码我们传进去的 a和b的类型与模板参数类型是匹配的,如果不匹配的话编译器会进行隐式类型转化,如果转化失败就报错。如下:

       类型转换成功:

template<typename L, typename R, typename RET>
RET add(L l, R r)
{
  return l + r;
}
int main()
{
  double a = 0.1;
    int b = 1;
  cout << add<int,int,int>(a, b) << endl;  //a:double类型隐式转换成int类型
}

image.gif

输出:

image.gif编辑

       类型转化失败:

template<typename L, typename R, typename RET>
RET add(L l, R r)
{
  return l + r;
}
class Date
{
  //
};
int main()
{
  int a = 0, b = 1;
  cout << add<int,Date,int>(a, b) << endl;  //a:类型转化失败
}

image.gif

输出:

E0304    没有与参数列表匹配的 函数模板 "add" 实例

2.4 模板参数的匹配原则

       一个函数的模板函数可以与非模板函数同时存在,并且在没有显式实例化且类型匹配的情况下会优先匹配非模板函数,如果是显式实例化才会调用模板,如下。

template<class T>
void swap(T& a, T& b)
{
  std::swap(a, b);
  std::cout << "我是模板 ";
  std::cout << a << ' ' << b << std::endl;
}
void swap(int& a, int& b)
{
  std::swap(a, b);
  std::cout << "我是非模板 ";
  std::cout << a << ' ' << b << std::endl;
}
int main()
{
  int a = 1, b = 2;
  std::cout << a << ' ' << b << std::endl;
  swap(a, b);   //类型匹配优先调用非模板函数
  swap<int>(a, b);  //调用模板实例化
}

image.gif

输出:

image.gif编辑

       当我们没有去写模板的时候,函数发生类型不匹配可能会进行隐式类型转换,但是有了模板之后就不会发生隐式类型转化,而是使用模板实例化出来一个更为合适的函数。如下:

       没有模板时会发生隐式实例化:

void test(int a, int b)
{
  std::cout << "我不是模板" << std::endl;
}
int main()
{
  test(2, 2.0);//2.0发生隐式类型转化成int;
}

image.gif

输出:

image.gif编辑

       存在模板不会发生隐式类型转化,而是使用模板实例化出来一个更为合适的函数:

template<class T1,class T2>
void test(T1 a, T2 b)
{
  std::cout << "我是模板" << std::endl;
}
void test(int a, int b)
{
  std::cout << "我不是模板" << std::endl;
}
int main()
{
  test(2, 2.0);//不发生隐式类型转化而是隐式实例化模板
}

image.gif

输出:

image.gif编辑

3. 类模板

3.1 类模板格式

       和函数模板一样,类模板是类的一个模具,类模板格式如下:

template

class 类模板名

{

   //类体

};

       我们普通类是可以声明和定义分离的,如果类模板要实现声明和定义分离,那么在定义的时候也要加上模板声明。(虽然可以实现分离但最好不要,尤其是声明和定义在不同文件时会由于函数没有被实例化而发生链接错误,具体到进阶部分和大家说明)如:

template<class T1, class T2>
class A
{
  ~A();  //声明
};
template<class T1,class T2> 
A<T1, T2>::~A()   //定义
{
  //...
}

image.gif

3.2 类模板的实例化

        类模板的实例化和函数模板相同,需要在类名后加上尖括号并且指定类型。注:模板名不是类,实例化后才是一个类。

template<class T>
class Date
{
  //..
};
int main()
{
  Date<int>;
}

image.gif


相关文章
|
8天前
|
编译器 C语言 C++
c++的学习之路:19、模板
c++的学习之路:19、模板
29 0
|
2天前
|
运维 Serverless Go
Serverless 应用引擎产品使用之在阿里云函数计算中c++模板,将编译好的C++程序放进去部署如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
9 1
|
2天前
|
存储 C++
【C++模板】模板实现通用的数组
【C++模板】模板实现通用的数组
|
6天前
|
编译器 程序员 C++
C++从入门到精通:3.1模板编程——提高代码的复用性和灵活性
C++从入门到精通:3.1模板编程——提高代码的复用性和灵活性
|
6天前
|
编译器 C语言 C++
【C++进阶(七)】仿函数深度剖析&模板进阶讲解
【C++进阶(七)】仿函数深度剖析&模板进阶讲解
|
7天前
|
存储 编译器 对象存储
【C++基础(十)】C++泛型编程--模板初阶
【C++基础(十)】C++泛型编程--模板初阶
【C++基础(十)】C++泛型编程--模板初阶
|
8天前
|
存储 编译器 Linux
c++的学习之路:8、内存管理与模板
c++的学习之路:8、内存管理与模板
8 0
|
18天前
|
编译器 C语言 C++
【C++初阶(九)】C++模版(初阶)----函数模版与类模版
【C++初阶(九)】C++模版(初阶)----函数模版与类模版
20 0
|
18天前
|
存储 编译器 Linux
【C++初阶(十)】C++模板(进阶) ---非类型模板参数、模板的特化以及模板的分离编译
【C++初阶(十)】C++模板(进阶) ---非类型模板参数、模板的特化以及模板的分离编译
21 0
|
18天前
|
存储 C++ 容器
【C++初阶】STL详解(十)set、map、multiset、multimap的介绍及使用
【C++初阶】STL详解(十)set、map、multiset、multimap的介绍及使用
27 0