C++ 学习系列 -- 模板 template

news2024/11/17 13:31:28

一  C++ 模板介绍?

  C++ 为什么引入模板?

      我的理解是:

C++ 引入模板的概念,是为了复用重复的代码,当某些代码除了操作的数据类型不同以外,其他逻辑全都相同,此时就适合采用模板的方式。

定义模板类或者模板函数时,只是定义了一个代码的架子,使用时需要配合上实际的数据类型,数据类型可以是基本数据类型也可以是用户自定义的类型。

官方一点的说法:

所谓模板,实际上是建立一个通用函数或类,其类内部的类型和函数的形参类型不具体指定,用一个虚拟的类型来代表。这种通用的方式称为模板。

模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。

这种模板的设计在许多编程语言中是由类似的,比如 Java 、Scala 中都有模板的概念。

二  class template 类模板

类模板例子:

// complex.h
template<typename T>
class Complex
{
public:
    Complex(T re = 0, T im = 0):m_re(re), m_im(im)
    {

    }

    Complex& operator+=(const Complex& other);

    T real() const
    {
        return m_re;
    }

    T imag() const
    {
        return m_im;
    }


private:
    T m_re;
    T m_im;
};


template<typename T>
Complex<T>& Complex<T>::operator+=(const Complex& other)
{
    this->m_re += other.m_re; // 任意一个类都是自己的 friend class,所以可以使用 private 成员变量
    this->m_im += other.m_im;
    return *this;
}

// main.cpp
#include"complex.h"

int main()
{
    Complex<double> c1(1, 2);
    Complex<int>    c2(1.1, 2.2);
    Complex<float>  c3(1.1, 2.2);

    return 0;
}

上面是一个复数 Complex模板类的简单实现。

三  function template 函数模板

// complex.h
template<typename T>
class Complex
{
public:
    Complex(T re = 0, T im = 0):m_re(re), m_im(im)
    {

    }

    bool operator<(const Complex& other) const
    {
        if(this->m_re < other.m_re)
        {
            return true;
        }
        else if(this->m_re > other.m_re)
        {
            return false;
        }
        else
        {
            if(this->m_im < other.m_im)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    T real() const
    {
        return m_re;
    }

    T imag() const
    {
        return m_im;
    }


private:
    T m_re;
    T m_im;
};

// main.cpp
#include <iostream>
#include"complex.h"

// 模板函数
template<typename T>
const T& myMin(const T& a, const T& b)
{
    return a < b ? a : b;
}

int main()
{
      int a1 = 11;
      int b1 = 22;
      std::cout << myMin(a1, b1) << std::endl;

      double a2 = 1.1;
      double b2 = 2.2;
      std::cout << myMin(a2, b2) << std::endl;
      
      Complex<int> c11(11, 2);
      Complex<int> c12(1, 22);

      auto cc = myMin(c11, c12);
      std::cout << cc.real() << ", " << cc.imag() << std::endl;
  
      return 0;
}


       以上代码定义了一个模板函数 myMin ;一个模板类 Complex,自定义了一个 operator< 成员函数。

      从代码中我们发现一个问题,模板类要用尖括号指明实际的元素类型,而模板函数并不需要尖括号指明实际的元素类型。这是因为在 C++ 中,模板函数在调用时,可以通过传入的实参推断出形参的类型,因而不需要指明实际的元素类型。

      模板函数 myMin 传入的类型需要有比较的能力,c++ 基本类型天然就具有比较的能力,而用户自定义类型需要实现 operator< 成员函数,才能在编译到 < 那行代码时能够通过,若是自定义类型没有实现 operator< 成员函数时,编译会出现错误。

四  member template 成员模板

成员模板也是一种函数模板,只不过针对的是构造函数的模板。

// base.h
class Base2
{

};

class Derive2:public Base2
{

};

class Base3
{
    
};

class Derive3:public Base3
{
    
};

// my_pair.h
template<typename T1, typename T2>
class MyPair
{
public:
    typedef T1 firstArgumentType;
    typedef T1 secondArgumentType;

    MyPair():first(T1()), second(T2())
    {
    }

    MyPair(const T1& arg1, const T2& arg2):first(arg1), second(arg2)
    {
    }

    // 成员模板
    template<typename U1, typename U2>
    MyPair(const MyPair<U1, U2>& other):first(other.first),second(other.second)
    {
    }
public:
    T1 first;
    T2 second;
};

// main.cpp
#include"my_pair.h"
#include"base.h"

int main()
{
    MyPair<Base2, Base3> pair1;
    MyPair<Derive2, Derive3> pair2;

    MyPair<Base2, Base3> pair3(pair2);

   return 0;
}

五  模板特化(specialization)与偏特化(partial specialization)

1. 模板特化 (specialization)

      模板特化指得是将模板类或者模板函数特化到某些特定的指定类型,使得在编译器编译时,实际编译执行的代码是特化后的版本。

 

// my_hash.h
#include<iostream>
#include<iostream>

template <class T>
struct my_hash
{
    T operator()(T x)
    {
        std::cout << "template <class T> struct my_hash" << std::endl;
        return x;
    }
};

// 以下为三个特化版本
template<>
struct my_hash<int>
{
    int operator()(int x)
    {
        std::cout << "template <> struct my_hash<int>" << std::endl;
        return x;
    }
};

template<>
struct my_hash<char>
{
    char operator()(char x)
    {
        std::cout << "template <> struct my_hash<char>" << std::endl;
        return x;
    }
};

template<>
struct my_hash<long>
{
    long operator()(long x)
    {
        std::cout << "template <> struct my_hash<long>" << std::endl;
        return x;
    }
};
// main.cpp

#include"my_hash.h"

int main()
{
    std::cout << my_hash<std::string>()("abcd") << std::endl;

    std::cout << my_hash<char>()('a') << std::endl;
    std::cout << my_hash<int>()(66) << std::endl;
    std::cout << my_hash<long>()(6666) << std::endl;

    return 0;
}

 输出:

2. 模板偏特化(partial specialization)

2.1  个数偏特化

      模板的个数变化时的特化版本

// my_vector.h
#include<iostream>

template<typename T, typename Alloc>
class my_vector
{
public:
    my_vector()
    {
        std::cout << "template<typename T, typename Alloc> class my_vector" << std::endl;
    }
};

template<typename Alloc>
class my_vector<bool, Alloc>
{
public:
    my_vector()
    {
        std::cout << "template<typename Alloc> class my_vector<bool, Alloc>" << std::endl;
    }
};

// main.cpp
#include"my_vector.h"

int main()
{
   my_vector<std::string, std::allocator<std::string>>  vec1;
   my_vector<bool, std::allocator<std::string>>  vec2;

   return 0;
}

输出:

2.2 范围偏特化

      通过特化模板类型的指针来实现指针的特化

// e.h
#include<iostream>

template<typename T>
class E
{
public:
    E()
    {
        std::cout << "template<typename T> class E" << std::endl;
    }
};

template<typename T>
class E<T*>
{
public:
    E()
    {
        std::cout << "template<typename T> class E<T*>" << std::endl;
    }
};

// main.cpp
#include"e.h"

int main()
{
    E<std::string> e1;
    E<std::string*> e2;
    
    return 0;
}

输出:

六  模板模板参数(template template parameter)

模板模板参数其实就是模板的其中参数又是另外一个模板参数,示例代码如下:

XCLS 类 的第一个模板参数是 T ,第二个模板参数是一个模板类 Container,我们可以定义一个 XX 模板类来组合使用 XCLS ,将 XX 传入即可;

也可以使用容器类 list  传入,但是如果直接传入编译是会出错的,比如下面

   XCLS<std::string, std::list> xcls3; 编译不过

  编译不过的原因是,std::list 实际上是两个模板参数,编译器检查时发现少给了一个模板参数 _Alloc ,所以编译不过。 

template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
    class list;

可以通过如下定义 Lst 来将模板类 list 的模板参数限定为 一个

template<typename T>
using Lst = std::list<T, std::allocator<T>>;
// xcls.h
#include<iostream>

template<typename T, template<typename U> class Container>
class XCLS
{
public:
    XCLS()
    {
        std::cout << "template<typename T, template<typename U> class Container> class XCLS" << std::endl;
    }
private:
    Container<T> c;
};

template<typename T>
class XX
{
public:
    XX()
    {
        std::cout << "template<typename T> class XX" << std::endl;
    }
};

// main.cpp
#include"xcls.h"

int main()
{  
   // 模板模板的参数使用
   XCLS<std::string, XX> xcls1;
   XCLS<std::string, Lst> xcls2;
   // XCLS<std::string, std::list> xcls3; 编译不过

   return 0;
}

输出:

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

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

相关文章

Linux的文件系统 内核结构

Linux的文件系统 Q1&#xff1a;什么是文件系统&#xff1f; A&#xff1a;在学术的角度下&#xff0c;文件系统指“操作系统用于明确存储设备组织文件的方法”&#xff0c;是“文件管理系统”的简称&#xff0c;本质也是代码&#xff0c;一段程序 Q2&#xff1a;文件系统&…

使用opencv的Canny算子实现图像边缘检测

1 边缘检测介绍 图像边缘检测技术是图像处理和计算机视觉等领域最基本的问题&#xff0c;也是经典的技术难题之一。如何快速、精确地提取图像边缘信息&#xff0c;一直是国内外的研究热点&#xff0c;同时边缘的检测也是图像处理中的一个难题。早期的经典算法包括边缘算子方法…

代码随想录-刷题第二十八天

93. 复原 IP 地址 题目链接&#xff1a;93. 复原 IP 地址 思路&#xff1a;切割问题&#xff0c;原理上还是抽象成树形结构&#xff0c;然后使用回溯法。 131 题的要求是&#xff1a;让你把字符串 s 切分成若干个合法的回文串&#xff0c;返回所有的切分方法。 本题的要求是…

知识|基于混合模式的多余度飞控全数字仿真系统研究

*余度&#xff08;Redundancy&#xff09;&#xff1a;一种确保安全的设计手段&#xff0c;使得出现两个及以上故障时&#xff0c;才会引起既定不希望发生的工作状态。 飞行控制软件主要完成飞行传感器数据处理、飞行姿态控制和余度管理任务&#xff0c;对保证飞机安全性和可靠…

GroupMixFormer:基于Group-Mix注意力的视觉Transformer

文章目录 摘要1、简介2、相关工作2.1、视觉转换器2.2、全面的自注意力建模 3、组混合注意力和GroupMixFormer3.1. 动机&#xff1a;从个体到群体3.2. GMA: 混合组以获得更好的注意力3.3. 架构配置 4、实验4.1、实现细节4.2. 与最先进模型的比较4.3. 消融实验 5、结论 摘要 htt…

[论文笔记] chatgpt系列 SparseMOE—GPT4的MOE结构

SparseMOE: 稀疏激活的MOE Swtich MOE,所有token要在K个专家网络中,选择一个专家网络。 显存增加。 Experts Choice:路由MOE:​​​​​​​ 由专家选择token。这样不同的专家都选择到某个token,也可以不选择该token。 由于FFN层的时间复杂度和attention层不同,FFN层的时…

[Vulnhub靶机] DC-1

[Vulnhub靶机] DC-1靶机渗透思路及方法&#xff08;个人分享&#xff09; 靶机下载地址&#xff1a; https://download.vulnhub.com/dc/DC-1.zip 靶机地址&#xff1a;192.168.67.25 攻击机地址&#xff1a;192.168.67.3 一、信息收集 1.使用 arp-scan 命令扫描网段内存活的…

代码随想录刷题题Day14

刷题的第十四天&#xff0c;希望自己能够不断坚持下去&#xff0c;迎来蜕变。&#x1f600;&#x1f600;&#x1f600; 刷题语言&#xff1a;C Day14 任务 ● 110.平衡二叉树 ● 257. 二叉树的所有路径 ● 404.左叶子之和 1 平衡二叉树 二叉树节点的深度&#xff1a;指从根节…

数据库系列之简要对比下GaussDB和OpenGauss数据库

GaussDB作为一款企业级的数据库产品&#xff0c;和开源数据库OpenGauss之间又是什么样的关系&#xff0c;刚开始接触的时候是一头雾水&#xff0c;因此本文简要对比下二者的区别&#xff0c;以加深了解。 1、GaussDB和OpenGauss数据库简要对比 GaussDB是华为基于PostgreSQL数据…

用Python快速从深层嵌套 JSON 中找到特定的 Value

有时候&#xff0c;我们拿到一个JSON数据的时候&#xff0c;会难以看出其逻辑层次结构。 这时候就需要我们进行代码解析了。 代码&#xff1a; import jsondef find_json_value(data_json, value, path""):if isinstance(data_json, dict):for k, v in data_json.…

学生用的台灯选什么比较好?热门考研护眼台灯推荐

是不是在台灯下用眼时间长了就觉得眼睛干涩难受&#xff0c;这说明是时候该换掉你的普通台灯换一盏护眼台灯了&#xff0c;用了才知道有多香。小台灯也有大学问&#xff0c;如果还不知道该怎么挑台灯的话也没关系&#xff0c;我已经总结了一份护眼台灯的科普和攻略&#xff0c;…

vmware安装Linux虚拟机设置固定IP地址

Linux虚拟机设置固定IP 近期工作需要&#xff0c;本地用VMware安装了三台Linux-centos虚拟机&#xff0c;来搭建rocketmq集群。但是IP地址每天关机重启之后就会比变化&#xff0c;很是恼火。找到了一个修改的教程&#xff0c;亲测有效&#xff01;&#xff01;&#xff01;&am…

MySQL数据存储、索引记录

行格式(每行记录) 行格式(每行记录)&#xff1a; 以记录为单位来向表中插入数据的&#xff0c;这些记录在磁盘上的存放方式也被称为 行格式 或者 记录格式。 InnoDB 存储引擎4种不同类型的 行格式 &#xff0c;分别是 Compact 、 Redundant 、Dynamic 和 Compressed 行格式。组…

电机的开环控制和闭环控制

目录 开环电机控制 闭环电机控制 开环到闭环转换 开环电机控制 开环控制&#xff08;也称为标量控制或伏特/赫兹控制&#xff09;是一种常见的电机控制方法&#xff0c;可用于运行任何交流电机。这是一种简单的方法&#xff0c;不需要来自电机的任何反馈。为了保持定子磁通恒…

软考、PMP 区别

软考 考试时间&#xff1a;一年两次&#xff0c;报名3月&#xff0c;8月&#xff1b;对应考试&#xff1a;5月最后一个周末&#xff0c;11月第一个周末 报名费&#xff1a;50-200元&#xff0c;每个城市不同北京57每科 报名网站&#xff1a;中国计算机技术职业资格网 考试等…

LangChain学习三:链-实战

文章目录 上一节内容&#xff1a;LangChain学习二&#xff1a;提示-实战&#xff08;下半部分&#xff09;学习目标&#xff1a;明白链是什么&#xff1f;有哪些&#xff1f;怎么用&#xff1f;学习内容一&#xff1a;介绍学习内容二&#xff1a;有那些学习内容三&#xff1a;实…

【Flink-cdc-Mysql-To-Kafka】使用 Flinksql 利用集成的 connector 实现 Mysql 数据写入 Kafka

【Flink-cdc-Mysql-To-Kafka】使用 Flinksql 利用集成的 connector 实现 Mysql 数据写入 Kafka 1&#xff09;环境准备2&#xff09;准备相关 jar 包3&#xff09;实现场景4&#xff09;准备工作4.1.Mysql4.2.Kafka 5&#xff09;Flink-Sql6&#xff09;验证 1&#xff09;环境…

EAM系统在地铁设备管理中的应用

在现代城市的交通系统中&#xff0c;地铁作为一种高效、快速、可靠的公共交通工具&#xff0c;扮演着至关重要的角色。为了确保地铁系统的正常运行和可靠性&#xff0c;地铁管理部门需要有效地管理大量的设备和设施。在这个过程中&#xff0c;企业资产管理&#xff08;EAM&…

虚幻学习笔记13—C++静态和动态加载

一、前言 我们在蓝图中可以很方便的添加各种需要的组件&#xff0c;那么在C代码中要如何实现呢。在代码中分静态和动态加载&#xff0c;而无论静态和动态&#xff0c;加载的内容有资源和资源类&#xff0c;资源类通常为带资源的蓝图类。 二、实现 在实现静态或动态加载时&…

mybatis-plus雪花算法自动生成ID到前端后精度丢失问题

问题发生 前端接收到后端的数据出现异常&#xff0c;异常如下&#xff1a; 如图这是后端正常返回的数据&#xff0c; 但是点击预览时发现这个id的数据被改变了 这就导致了我通过id去修改相关数据时无法成功 问题原因 id的长度过长&#xff08;19位&#xff09;&#xff0c;前…