【C++】模版进阶

【C++】模版进阶

目录

  • 【C++】模版进阶
      • 模版的特殊情况
      • 非类型模版参数
          • C++11中的array
      • 模版的特化
          • 概念
          • 函数模版特化
          • 类模版特化
      • 模版分离编译
          • 分离编译
            • 解决办法
      • 模版总结

作者:爱写代码的刚子
时间:2023.7.25
前言:本篇博客是对

模版的特殊情况

如果一个人没有深入了解模版就很可能写出以下代码:
【C++】模版进阶_第1张图片
错误信息:
【C++】模版进阶_第2张图片
解决方法(加上typename关键字,这也是模版关键字typename和class的区别):
【C++】模版进阶_第3张图片
【解释原理】:
由于 模版参数container没有实例化(注意) ,编译器在检查时无法确认container是静态成员变量或者对象(静态成员也是可以用类域去访问)或者typedef的内嵌类型等还是模版参数。(总的来说如果不加typename编译器无法区分contain是类型还是对象)这里的container按理来说应该是类型。

typename的作用就是告诉编译器container就是一个类型,等模版实例化再去找,这样编译器才能继续编译。
当然,还有另一种解决方法:
用auto可以替代typename的作用:
【C++】模版进阶_第4张图片

附:
取类模版的内嵌类型,且这个类模版没有被实例化(只要取没有被实例化的类里面的东西,编译器就不能区分是变量对象还是类型)
在这里插入图片描述

非类型模版参数

模板参数分:类型形参与非类型形参。
类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参:用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

举例:当我们想要创建一个静态的栈时我们就可以使用非类型模版参数

【C++】模版进阶_第5张图片

注意:这里的参数N是常量,并不能被更改。

【注意】:

  • 浮点数、类对象以及字符串是不允许作为非类型模板参数的。(非类型模版参数为常量,并且必须是整形)
  • 非类型的模板参数必须在编译期就能确认结果。
C++11中的array

【C++】模版进阶_第6张图片

array中并没有插入等操作,因为array已经将空间开好了。
【C++】模版进阶_第7张图片

  • array并不会帮你初始化
  • array有迭代器,并且支持at函数(能严格检查越界)(但是还是建议使用vector)
    array函数查询

模版的特化

概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果。此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。

函数模版特化

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表:必须要和模版函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。

函数模版特化:
【C++】模版进阶_第8张图片

函数模版特化前要有一个匹配的模版,不然报错:
【C++】模版进阶_第9张图片

【注意】:
一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。
比如下面的代码,也可以用函数重载来代替模版特化:
【C++】模版进阶_第10张图片
(模版一般有现成的就用现成的)

类模版特化
    • 全特化
      全特化:将模版参数列表中所有的参数都确定化。
    • 偏特化
      偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。
      【C++】模版进阶_第11张图片

注意无论是全特化还是偏特化,其模版参数数量必须符合第一个模版声明时的参数数量。
偏特化有以下两种表现方式:

    • 部分特化
      将模版参数类表中的一部分参数特化。
    • 参数更进一步的限制
      偏特化并不仅仅是指特化部分参数,而是针对模版参数更进一步的条件限制所设计出来的一个特化版本。
  1. 萃取、哈希表也会用到特化操作。
  2. 特化的模版参数也可以是指针或者引用。

模版分离编译

分离编译

概念:
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。

先来看看经典错误:
stack_test.h文件:
【C++】模版进阶_第12张图片
stack_test.cpp文件:
【C++】模版进阶_第13张图片
main.cpp文件:
【C++】模版进阶_第14张图片
【运行错误】:
【C++】模版进阶_第15张图片
从运行结果来看很容易发现是链接错误。

【问题】:编译过程没有问题,但是链接的过程出现了问题,无法运行。
【解释】:由于模版的声明和定义分离,然而在stack_test.cpp文件中,模版参数并没有实例化(注意),编译器无法生成push和pop函数的地址。所以stack_test.h虽然有方法的声明,但是却找不到方法实现的地址。

模版的分离编译原理

解决办法
  1. 将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的。推荐使用这种。
    同一个文件下声明和定义分离:
    【C++】模版进阶_第16张图片

  2. 模板定义的位置显式实例化。这种方法不实用,不推荐使用。

【C++】模版进阶_第17张图片
在stack_test.cpp文件中将Stack类模版显示实例化,但是如果该模版要被很多不同的模版参数使用时,需要不同的多种显示实例化,所以不推荐

分离编译扩展(刘未鹏)

模版总结

【优点】

  1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
  2. 增强了代码的灵活性(适配器、仿函数)

【缺陷】

  1. 模板会导致代码膨胀问题,也会导致编译时间变长
  2. 出现模版编译错误时,错误信息非常凌乱,不易定位错误

模版进阶部分结束

你可能感兴趣的:(C++初阶,c++,java,开发语言,模版)