【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


相关文章
|
3月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
126 10
|
2月前
|
安全 编译器 C++
【C++11】可变模板参数详解
本文详细介绍了C++11引入的可变模板参数,这是一种允许模板接受任意数量和类型参数的强大工具。文章从基本概念入手,讲解了可变模板参数的语法、参数包的展开方法,以及如何结合递归调用、折叠表达式等技术实现高效编程。通过具体示例,如打印任意数量参数、类型安全的`printf`替代方案等,展示了其在实际开发中的应用。最后,文章讨论了性能优化策略和常见问题,帮助读者更好地理解和使用这一高级C++特性。
60 4
|
2月前
|
算法 编译器 C++
【C++】模板详细讲解(含反向迭代器)
C++模板是泛型编程的核心,允许编写与类型无关的代码,提高代码复用性和灵活性。模板分为函数模板和类模板,支持隐式和显式实例化,以及特化(全特化和偏特化)。C++标准库广泛使用模板,如容器、迭代器、算法和函数对象等,以支持高效、灵活的编程。反向迭代器通过对正向迭代器的封装,实现了逆序遍历的功能。
36 3
|
2月前
|
编译器 C++
【c++】模板详解(1)
本文介绍了C++中的模板概念,包括函数模板和类模板,强调了模板作为泛型编程基础的重要性。函数模板允许创建类型无关的函数,类模板则能根据不同的类型生成不同的类。文章通过具体示例详细解释了模板的定义、实例化及匹配原则,帮助读者理解模板机制,为学习STL打下基础。
33 0
|
3月前
|
编译器 程序员 C++
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
22 1
|
3月前
|
存储 编译器 C++
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
55 9
|
3月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
72 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
3月前
|
算法 编译器 C++
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
98 2
|
3月前
|
存储 算法 编译器
【C++】初识C++模板与STL
【C++】初识C++模板与STL
|
3月前
|
编译器 C++
【C++】模板进阶:深入解析模板特化
【C++】模板进阶:深入解析模板特化
116 0