C++基础(十七):模板进阶

news2024/9/17 9:11:53

      前面我们学习过模板,这一节我们对模板进行进阶的学习。

目录

一、非类型模板参数

二、类模板的特化

2.1 概念

2.2 函数模板特化

2.3 类模板特化

2.3.1 全特化

2.3.2 偏特化

三、模板的分离编译

3.1 什么是分离编译

3.2 模板的分离编译

3.3 解决方法

3.4. 模板总结

3.4.1 优点

3.4.2 缺点


一、非类型模板参数

模板参数分类类型形参与非类型形参。

  1. 类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
  2. 非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。相当于宏定义,直接进行替换。整型最为常用
namespace b
{
     // 定义一个模板类型的静态数组
     template<class T, size_t N = 100>
     class array
     {
          public:
              T& operator[](size_t index)
              {
                  return _array[index];
              }


              const T& operator[](size_t index)   const
              {
                   return _array[index];
              }
 
              size_t size()    const
             {
                    return _size;
             }


             bool empty()    const
             {
                    return 0 == _size;
             }
 
          private:
              T _array[N];
              size_t _size;
  };


}

注意:

  1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
  2. 非类型的模板参数必须在编译期就能确认结果。

二、类模板的特化

2.1 概念

       通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板。

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
      return left < right;
}


int main()
{
     cout << Less(1, 2) << endl; // 可以比较,结果正确
     Date d1(2022, 7, 7);
     Date d2(2022, 7, 8);
     cout << Less(d1, d2) << endl; // 可以比较,结果正确
     Date* p1 = &d1;
     Date* p2 = &d2;
     cout << Less(p1, p2) << endl; // 可以比较,结果错误
     return 0;
}

可以看到,Less绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果。上述示例中,p1指 向的d1显然小于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内容,而比较的是p1和p2指 针的地址,这就无法达到预期而错误。

此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。

2.2 函数模板特化

函数模板的特化步骤:

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


// 对Less函数模板进行特化(对特定的类型进行的特殊化处理)
template<>
bool Less<Date*>(Date* left, Date* right)
{
        return *left < *right;
}
int main()
{
       cout << Less(1, 2) << endl;
       Date d1(2022, 7, 7);
       Date d2(2022, 7, 8);
       cout << Less(d1, d2) << endl;
       Date* p1 = &d1;
       Date* p2 = &d2;
       cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
       return 0;
}

注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给 出。

bool Less(Date* left, Date* right)
{
     return *left < *right;
}

该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给 出,因此函数模板不建议特化。

2.3 类模板特化

2.3.1 全特化

全特化即是将模板参数列表中所有的参数都确定化。

template<class T1, class T2>
class Data
{
    public:
        Data() {cout<<"Data<T1, T2>" <<endl;}
   private:
        T1 _d1;
        T2 _d2;
};



template<>
class Data<int, char>    //类模板全特化
{
      public:
          Data() {cout<<"Data<int, char>" <<endl;}
      private:
           int _d1;
           char _d2;
};


void Test()
{
    Data<int, int> d1;
    Data<int, char> d2;
}

2.3.2 偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类:

template<class T1, class T2>
class Data
{
   public:
        Data() {cout<<"Data<T1, T2>" <<endl;}
   private:
        T1 _d1;
        T2 _d2;
};

偏特化有以下两种表现方式:

部分特化:

         将模板参数类表中的一部分参数特化。

// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
   public:
         Data() {cout<<"Data<T1, int>" <<endl;}
  private:
         T1 _d1;
         int _d2;
};

参数更进一步的限制

       偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。


//两个参数偏特化为指针类型
template <class T1, class T2>
class Data <T1*, T2*>
{ 
    public:
           Data() {cout<<"Data<T1*, T2*>" <<endl;}
 
    private:
         T1 _d1;
         T2 _d2;
};


//两个参数偏特化为引用类型
template <class T1, class T2>
class Data <T1&, T2&>
{
    public:
            Data(const T1& d1, const T2& d2)
            : _d1(d1)
            , _d2(d2)
            {
                 cout<<"Data<T1&, T2&>" <<endl;
            }
 
    private:
           const T1 & _d1;
           const T2 & _d2; 
 };


void test2 () 
{
         Data<double , int> d1; // 调用特化的int版本
         Data<int , double> d2; // 调用基础的模板 
         Data<int *, int*> d3; // 调用特化的指针版本
         Data<int&, int&> d4(1, 2); // 调用特化的指针版本
}

三、模板的分离编译

3.1 什么是分离编译

         一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。即:项目工程中一般将函数的声明或者类的声明放到.h文件,将函数的定义或者类的定义放到.cpp文件中,那么为什么要分离编译?方便查看和维护。

3.2 模板的分离编译

     假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:

// a.h
template<class T>
T Add(const T& left, const T& right);


// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
    return left + right;
}


// main.cpp
#include"a.h"
int main()
{
    Add(1, 2);
    Add(1.0, 2.0);
 
    return 0;
}

同样是分离编译,普通函数/类可以分离编译,函数模板和类模板的声明和定义不能分离,否则会发生报错!!!为什么???

 面试题:     

         C/C++程序要运行,一般要经历以下步骤:
预处理---->编译--->汇编---->链接
1、预处理:展开头文件,进行宏替换,条件编译,去掉注释,生成 .i文件;

2、编译:检查语法,生成汇编代码,生成  .s文件;

3、汇编:将汇编代码转成二进制的机器码,生成.o文件;

4、链接:将多个目标文件合并成一个,编译时函数有声明,随意编译会通过,链接时要去目标文件中找函数的地址(符号表),对于普通函数可以找到,因此,链接会通过。但是对于函数模板/类模板,模板的实例化发生在编译期。编译器需要在看到模板的定义和其使用的上下文时才能实例化模板。这意味着编译器需要知道模板的定义和所有类型参数的具体类型,因此,编译阶段不会实例化函数,符号表也就不会生成对应的函数地址,这样链接就会发生错误!!!生成a.out文件

3.3 解决方法

1. 将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。推荐使用这种。

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

3.4. 模板总结

3.4.1 优点

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

3.4.2 缺点

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

至此,这一讲内容介绍完毕,内容简单,星光不问赶路人,加油吧,感谢阅读,如果对此专栏感兴趣,点赞加关注!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1918804.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

医院人员管理系统03_下午:C3P0连接池,完成简单的增删改查

文章目录 什么是C3P0项目目录Students.javaC3P0Conn.javaStuDao.java套路代码 什么是C3P0 C3P0连接池要比jdbc更简单&#xff0c;dao层写方法就能看出来 项目目录 Students.java 没有变&#xff0c;直接是jdbc的实体类 跳转我的上一篇文章查看实体类代码 C3P0Conn.java 这…

AmazonS3部署以及nacos配置参数

AmazonS3部署 因为涉及到做的需求的头像的处理&#xff0c;所以需要去找头像的来源&#xff0c;没想到又是我们的老熟人&#xff0c;AmazonS3&#xff0c;巧了已经是第二次用了&#xff0c;上次我是用的别人的工具类去干的&#xff0c;这一次我这边自己编辑具体工具类型。 对应…

敏捷专家CSM认证培训内容概述(附2024年开班时间表)

敏捷专家CSM认证培训是专为希望在Scrum项目中担任Scrum Master角色的个人而设计的专业培训。CSM认证&#xff0c;全称Certified Scrum Master&#xff0c;是敏捷开发领域中备受认可的证书&#xff0c;由Scrum Alliance颁发。以下是对敏捷专家CSM认证培训的详细介绍&#xff1a;…

solidity实战练习1

//SPDX-License-Identifier:MIT pragma solidity ^0.8.24; contract PiggyBank{constructor()payable{emit Deposit(msg.value);//触发事件1//意味着在部署合约的时候&#xff0c;可以向合约发送以太币&#xff08;不是通过调用函数&#xff0c;而是直接在部署合约时发送&#…

JAVA中的回溯算法解空间树,八皇后问题以及骑士游历问题超详解

1.回溯算法的概念 回溯算法顾名思义就是有回溯的算法 回溯算法实际上一个类似枚举的搜索尝试过程&#xff0c;主要是在搜索尝试过程中寻找问题的解&#xff0c;当发现已不满足求解条件时&#xff0c;就“回溯”返回&#xff0c;尝试别的路径。回溯法是一种选优搜索法&#xff…

Python 神器:wxauto 库——解锁微信自动化的无限可能

&#x1f4dd;个人主页&#x1f339;&#xff1a;誓则盟约 ⏩收录专栏⏪&#xff1a;机器学习 &#x1f921;往期回顾&#x1f921;&#xff1a;“探索机器学习的多面世界&#xff1a;从理论到应用与未来展望” &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f…

短视频矩阵:批量发布的秘密揭秘

在数字化时代&#xff0c;短视频已经成为一种广受欢迎的媒体形式。无论是用于品牌推广、产品营销还是个人创作&#xff0c;短视频都提供了一种直观、生动的方式来吸引观众的注意力。然而&#xff0c;有效地制作、管理和发布短视频对于许多创作者和企业来说是一个挑战。 为此&am…

【Django】报错‘staticfiles‘ is not a registered tag library

错误截图 错误原因总结 在django3.x版本中staticfiles被static替换了&#xff0c;所以这地方换位static即可完美运行 错误解决

旧衣回收小程序开发,提高回收效率,实现创收

随着人们生活水平的提高&#xff0c;对穿衣打扮也越来越重视&#xff0c;衣服更换频率逐渐增高&#xff0c;旧衣回收行业因此产生&#xff0c;并随着市场规模的扩大&#xff0c;拥有了完善的回收产业链&#xff0c; 旧衣回收行业的发展不仅能够让大众获得新的赚钱方式&#xf…

探索创意无限:精彩APP启动页设计案例汇总

启动页是应用启动时出现的过渡页面或动画&#xff0c;旨在提升用户体验、缓解用户焦虑&#xff0c;以及传递品牌或产品的人情味。根据应用性能&#xff0c;其停留时间可能从1秒到3秒不等。启动页同样是应用名片&#xff0c;需要展现品牌的独特个性。运用品牌颜色和 IP 形象能加…

C语言 指针和数组——指针数组的应用:命令行参数

目录 命令行参数 演示命令行参数与main函数形参间的关系 命令行参数  什么是 命令行参数&#xff08; Command Line Arguments &#xff09;&#xff1f;  GUI 界面之前&#xff0c;计算机的操作界面都是字符式的命令行界面 &#xff08; DOS 、 UNIX 、 Linux &…

215.Mit6.S081-实验三-page tables

在本实验室中&#xff0c;您将探索页表并对其进行修改&#xff0c;以简化将数据从用户空间复制到内核空间的函数。 一、实验准备 开始编码之前&#xff0c;请阅读xv6手册的第3章和相关文件&#xff1a; kernel/memlayout.h&#xff0c;它捕获了内存的布局。kernel/vm.c&…

什么是渲染:两种渲染类型、工作原理

如果您是网页设计师或数字艺术家&#xff0c;您可能熟悉渲染过程的概念。这是数字艺术中的重要步骤&#xff0c;帮助您将图形模型转换为最终结果。在本文中&#xff0c;您将了解数字艺术中的渲染是什么、它的工作原理以及它的类型。 一、什么是渲染? 渲染是使用计算机软件对数…

怎么样的主食冻干算好冻干?品质卓越、安全可靠的主食冻干分享

当前主食冻干市场产品质量参差不齐。一些品牌过于追求营养数据的堆砌和利润的增长&#xff0c;却忽视了猫咪健康饮食的基本原则&#xff0c;导致市场上出现了以肉粉冒充鲜肉、修改产品日期等不诚信行为。更令人担忧的是&#xff0c;部分产品未经过严格的第三方质量检测便上市销…

Python实现傅里叶级数可视化工具

Python实现傅里叶级数可视化工具 flyfish 有matlab实现&#xff0c;我没matlab&#xff0c;我有Python&#xff0c;所以我用Python实现。 整个工具的实现代码放在最后,界面使用PyQt5开发 起源 傅里叶级数&#xff08;Fourier Series&#xff09;由法国数学家和物理学家让-巴…

Apache网页优化(企业网站结构部署与优化)

本章结构 一、Apache网页优化 在使用 Apache 作为 Web 服务器的过程中&#xff0c;只有对 Apache 服务器进行适当的优化配置&#xff0c;才能让 Apache 发挥出更好的性能。反过来说&#xff0c;如果 Apache 的配置非常糟糕&#xff0c;Apache可能无法正常为我们服务。因此&…

链接报错undefined reference to + libc++和libstdc++

1 问题现象 subscribe(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) 描述&#xff1a;编译的时候&#xff0c;最后的链接中一直没成功 2 可能原因 2.1 链接时缺失了相关目标文件&#xff08;.o&#x…

Visual Studio 2022 安装及使用

一、下载及安装 VS 官网&#xff1a;Visual Studio: IDE and Code Editor for Software Developers and Teams 下载免费的社区版 得到一个.exe文件 右键安装 选择C开发&#xff0c;并修改安装位置 等待安装 点击启动 二、VS的使用 1.创建项目 打开VS&#xff0c;点击创建新项…

1招搞定maven打包空间不足问题

目录 一、工具应用问题 二 、使用效果 三、使用方法 四、练习手段 一、工具应用问题 使用maven的package功能打包失败&#xff0c;报错“Java heap space”错误。 二 、使用效果 修改IDEA中maven内存使用大小后&#xff0c;打包成功。 三、使用方法 点击菜单“File->Set…

openWrt(4) - uci

uci show 1) uci show - 查看所有配置文件列表 2)查看特定配置文件的详细信息&#xff1a; uci show network 我们以 network 为例 3&#xff09;查看特定配置项的详细信息&#xff1a; uci show network.wan 添加一个新的配置条目&#xff1a;uci add network interface …