《C++ Primer》读书笔记第十六章-1-定义模板

2023-11-06

笔记会持续更新,有错误的地方欢迎指正,谢谢!

这一章特别实用,神器—>模板

泛型编程能处理在编译之前类型不知道的情况,在编译时获知类型,比如我们学过的容器、迭代器和算法都是泛型编程。

模板是C++中泛型编程的基础,记住:一个模板就是一个创建类或函数的蓝图。

定义模板

函数模板

要实现一个功能:比较两个值的大小。这两个值可能是int,, double, string等等,如果我们写出这些函数就会发现,它们除了参数类型不同,其他都一样。这样很麻烦,我们就可用模板实现泛型编程(泛型:类型很泛,不针对某一种类型)。

//这是一个函数模板
template <typename T> //以关键字template开始,
//后跟的是模板参数列表(不能为空),用逗号分割的一个或多个模板参数
int compare(const T &v1, const T &v2)
{
    if(v1<v2){return -1;}
    if(v2<v1){return 1;}
    return 0;
}
//这里写了一个函数蓝图,当实际去调用它时,编译器会根据实参来生成相应的函数:
compare(1, 0); //实例化出int compare(const int&, const int&)
compare(2.5, 3.8); //实例化出int compare(const double&, const double&)
compare("abc", "def"); //实例化出int compare(const string&, const string&)

模板类型参数

也就是T,T在后面编译时就被实参类型替代了,它在模板中可被用在任何地方:返回类型、函数参数类型、声明变量等,就跟变量类型一样用:

template <typename T>
T foo(T* p)
{
    T temp= *p;
    return temp;
}

非类型模板参数

模板参数列表不一定非要放类型模板参数,还可以放非类型模板参数,只不过这个非类型模板参数是用来表示具体的一个值而不是一个类型:

//当非类型参数在编译被替代时,一定是常量表达式,毕竟它是值,不是类型。
template <unsigned N, unsigned M>
int compare(const char (&p1)[N], const char (&p2)[M])
{
    return strcmp(p1, p2);
}

模板非类型参数是一个常量,所以有:

  1. 如果它是整型参数,必须是常量表达式
  2. 如果是指针或者引用,它所绑定的对象必须有静态生存期

inline和constexpr的函数模板

这两个还是可以使用函数模板的

constexpr知识补充:

被constexpr修饰的一定是常量,必须用常量表达式(字面值类型,包括算术类型、引用、指针)或constexpr函数(constexpr函数是足够简单以使得编译时就可以计算其结果)初始化。
例子:

constexpr int a = 20;//对 
constexpr int b = a + 1;//对 
constexpr int sz = size();//可对可错,只有当size是一个constexpr函数时才对。

constexpr是编译时检查它修饰的是不是常量,所以,若你认定一个变量是一个常量表达式,那就把它声明为constexpr类型。

模板编译

当编译器遇到一个模板定义时,它并不生成代码,只有当我们实例化出模板的一个特定版本时,编译器才会生成代码。

由于实例化时需要掌握函数模版和类模版成员函数的定义,其定义通常也放在头文件中,与普通函数和普通类成员函数不同。

个人感觉:模板就像Unity里的Prefab预制体,需要用的时候拿来根据自己的需要进行实例化。

类模板

与函数模板不同,编译器不能为类模板推断参数的类型,必须显示提供类型。

定义类模板

作为例子,我们将实现Blob,它不再只针对string,而是作为一个类模板,可以用于更多类型的元素:

//有些成员函数只写了声明,后面会再去实现定义的
template <typename T> 
class Blob
{
public:
    typedef T value_type;
    typedef typename vector<T>::size_type size_type;

    //构造函数
    Blob();
    Blob(initializer_list<T> il);

    //Blob中的元素数目
    size_type size() const {return data->size();}
    bool empty() const {return data->empty();}

    //添加删除元素
    void push_back(const T &t) {data->push_back(t);}
    void push_back(T &&t) {data->push_back(std::move(t));} //移动版本
    void pop_back();

    //元素访问
    T& back();
    T& operator[](size_type i);

private:
    shared_ptr<vector<T>> data;
    //检查错误,若i违法,抛出错误msg
    void check(size_type i, const string &msg) const
};

我就知道你typedef typename vector<T>::size_type size_type;看不懂,所以暖心提供链接:http://blog.csdn.net/yyxyong/article/details/78632340

实例化类模板

必须提供显式模板实参,每种元素类型生成的各个实例类之间无关联,拥有各自的static成员。

Blob<int> ia; //空Blob<int>

类中的T由int代替。你给什么类型,编译器就会实例化出对应类型的类。
上面这句代码包含了两个实例化过程:

  1. 类模板实例化为类
  2. 类实例化为对象

模板嵌套

比如:在自定义的一个模板类中使用了vector。vector也是一个模板类。

Blob构造函数

//默认构造函数
template <typename T>
Bolb<T>::Blob() : data(make_shared<vector<T>>()){}

//接受参数的转换构造函数
template <typename T>
Blob<T>::Blob(initializer_list<T> il) : data(make_shared<vector<T>>(il)) {}

类模板成员函数的实例化

实例化了的类模板,其成员函数只有在用到时才被实例化。

模板参数

我们通常将类型参数命名为T,其实我们可以使用任何名字。

模板参数作用域:

在模板内不能重用模板参数的名字,正常的名字隐藏规则依旧适用。比如:template<typename V,typename V>是错误的!

默认模板实参:

旧标准只允许类模板有默认实参,新标准允许函数模板也有默认实参。

就像我们能为函数参数提供默认实参一样,我们也可以提供默认模板实参,如下方的less。

1.我们先来给函数模板提供默认实参:

//默认使用标准库的less函数对象模板
//此时,有一个默认模板实参less<T>和一个默认函数实参F()
template <typename T, typename F = less<T>>
int compare(const T &v1, const T &v2, F f = F())//定义了一个新函数参数f
{
    if(f(v1, v2)){return -1;}
    if(f(v2, v1)){return 1;}
    return 0;
}

2.我们再来给类模板提供默认实参:
调用类模板时必须加上尖括号,即使所有参数都有默认值。

template <class T = int>
class Numbers
{
public:
    Numbers(T v = 0) : val(v) {}//构造函数
};
//因为有了默认实参int,我们就可以这么干了
Numbers<> a;

成员模板

类里面的 本身是模板的成员函数 是模板函数,这个类可以是普通类也可以是类模板,下面就分别来讲:

普通(非类模板)类的成员模板

class Debugdel
{
public:
    template <typename T>
    void operator()(T *p) const //虽然是这个类的对象,但可以删除任何类型的指针
    {
        delete p;
    }
};

类模板的成员模板

我们要为Blob类定义一个构造函数,接受俩迭代器,表示要拷贝的元素范围,由于我们希望支持不同类型的迭代器,因此要将这个构造函数定义为模板:

template <typename T> 
class Blob
{
    template <typename It>
    Blob(It b, It e);//构造函数模板
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《C++ Primer》读书笔记第十六章-1-定义模板 的相关文章

  • 静态只读字符串数组

    我在我的 Web 应用程序中使用静态只读字符串数组 基本上数组有错误代码 我将所有类似的错误代码保存在一个数组中并检查该数组 而不是检查不同常量字符串中的每个错误代码 like public static readonly string m
  • 如何从 C# 中的 dataTable.Select( ) 查询中删除单引号?

    所以我有一个经销商名称列表 我正在我的数据表中搜索它们 问题是 一些傻瓜必须被命名为 Young s 这会导致错误 drs dtDealers Select DealerName dealerName 所以我尝试替换字符串 尽管它对我不起作
  • 计算 XML 中特定 XML 节点的数量

    请参阅此 XML
  • 使用 C# 登录《我的世界》

    我正在尝试为自己和一些朋友创建一个简单的自定义 Minecraft 启动器 我不需要启动 Minecraft 的代码 只需要登录的实际代码行 例如 据我所知 您过去可以使用 string netResponse httpGET https
  • 如何捕获未发送到 stdout 的命令行文本?

    我在项目中使用 LAME 命令行 mp3 编码器 我希望能够看到某人正在使用什么版本 如果我只执行 LAME exe 而不带参数 我会得到 例如 C LAME gt LAME exe LAME 32 bits version 3 98 2
  • 在c#中执行Redis控制台命令

    我需要从 Redis 控制台获取 客户端列表 输出以在我的 C 应用程序中使用 有没有办法使用 ConnectionMultiplexer 执行该命令 或者是否有内置方法可以查找该信息 CLIENT LIST是 服务器 命令 而不是 数据库
  • IdentityServer 4 对它的工作原理感到困惑

    我阅读和观看了很多有关 Identity Server 4 的内容 但我仍然对它有点困惑 因为似乎有很多移动部件 我现在明白这是一个单独的项目 它处理用户身份验证 我仍然不明白的是用户如何注册它 谁存储用户名 密码 我打算进行此设置 Rea
  • 如何使用 Castle Windsor 将对象注入到 WCF IErrorHandler 实现中?

    我正在使用 WCF 开发一组服务 该应用程序正在使用 Castle Windsor 进行依赖注入 我添加了一个IErrorHandler通过属性添加到服务的实现 到目前为止一切正常 这IErrorHandler对象 一个名为FaultHan
  • C# 数据表更新多行

    我如何使用数据表进行多次更新 我找到了这个更新 1 行 http support microsoft com kb 307587 my code public void ExportCSV string SQLSyntax string L
  • Python 属性和 Swig

    我正在尝试使用 swig 为一些 C 代码创建 python 绑定 我似乎遇到了一个问题 试图从我拥有的一些访问器函数创建 python 属性 方法如下 class Player public void entity Entity enti
  • 在Linux中,找不到框架“.NETFramework,Version=v4.5”的参考程序集

    我已经设置了 Visual studio 来在我的 Ubuntu 机器上编译 C 代码 我将工作区 我的代码加载到 VS 我可以看到以下错误 The reference assemblies for framework NETFramewo
  • 在视口中查找 WPF 控件

    Updated 这可能是一个简单或复杂的问题 但在 wpf 中 我有一个列表框 我用一个填充数据模板从列表中 有没有办法找出特定的数据模板项位于视口中 即我已滚动到其位置并且可以查看 目前我连接到了 listbox ScrollChange
  • 在mysql连接字符串中添加应用程序名称/程序名称[关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我正在寻找一种解决方案 在连接字符串中添加应用程序名称或程序名称 以便它在 MySQL Workbench 中的 客户端连接 下可见 SQL
  • 检测到严重错误 c0000374 - C++ dll 将已分配内存的指针返回到 C#

    我有一个 c dll 它为我的主 c 应用程序提供一些功能 在这里 我尝试读取一个文件 将其加载到内存 然后返回一些信息 例如加载数据的指针和内存块的计数到 c Dll 成功将文件读取到内存 但在返回主应用程序时 程序由于堆损坏而崩溃 检测
  • 在 Windows Phone silverlight 8.1 上接收 WNS 推送通知

    我有 Windows Phone 8 1 silverlight 应用程序 我想使用新框架 WNS 接收通知 我在 package appxmanifest 中有
  • 使用 C 在 OS X 中获取其他进程的 argv

    我想获得其他进程的argv 例如ps 我使用的是在 Intel 或 PowerPC 上运行的 Mac OS X 10 4 11 首先 我阅读了 ps 和 man kvm 的代码 然后编写了一些 C 代码 include
  • 我可以在“字节数”设置为零的情况下调用 memcpy() 和 memmove() 吗?

    当我实际上没有什么可以移动 复制的时候 我是否需要处理这些情况memmove memcpy 作为边缘情况 int numberOfBytes if numberOfBytes 0 memmove dest source numberOfBy
  • Objective-C / C 给出枚举默认值

    我在某处读到过关于给枚举默认值的内容 如下所示 typedef enum MarketNavigationTypeNone 0 MarketNavigationTypeHeirachy 1 MarketNavigationTypeMarke
  • 灵气序列解析问题

    我在使用 Spirit Qi 2 4 编写解析器时遇到一些问题 我有一系列键值对以以下格式解析
  • OpenCV SIFT 描述符关键点半径

    我正在深入研究OpenCV的SIFT描述符提取的实现 https github com Itseez opencv blob master modules nonfree src sift cpp 我发现了一些令人费解的代码来获取兴趣点邻域

随机推荐

  • Springboot集成websocket实时刷新数据大屏,利用quartz做定时处理(数据可视化大屏),亲测可用

    对于实时动态的数据大屏 我们以前一般都是通过ajax定时轮询去更新数据 实现数据的实时性 这样做对服务器是不友好的 长期频繁的请求会造成服务器压力 同样会造成过多不必要的请求 浪费流量和服务器资源 WebSocket工作流程 客户端 浏览器
  • 波士顿房价预测——线性回归模型

    1 案例背景介绍 数据介绍 该数据是UCI ML房价数据集的副本 以下特征是专家得出的影响房价的结果属性 所以可以直接使用 但很多时候需要我们自己去量化特征找出需要的属性 2 处理流程 基本数据处理 数据很规整 没有需要特别处理的 我们确定
  • VPN的搭建与使用--CentOS7.9(OpenVpn环境配置)

    一 VPN概述 OpenVpn VPN是Virtual Private Network 虚拟私人网络 的缩写 它是一种通过公用网络建立安全 加密通信的技术 VPN的主要功能是创建一个虚拟的专用网络 使得用户可以在这个网络中访问外部网络资源
  • 谁说dubbo接口只能Java调用,我用Python也能轻松稿定

    由于公司使用基于Java语言的Dubbo技术栈 而本人对Python技术栈更为熟悉 为了使不懂JAVA代码的同学也能进行Dubbo接口层的测试 总结一个通过python实现dubbo接口调用的实现方案 01 实现原理 根据Dubbo官方文档
  • AI产业链的划分,主要可分为基础层、技术层和应用层

    基础层 基础层又分为数据层和计算能力 数据层像自身能够不断产生数据的Alphago一样 他会自己不断的产生数据和进化它的模型 计算能力就像我之前提到的硬件的加速 神经网络芯片这一类的计算能力提供商 也有做像GPU云计算的开放给创业公司 技术
  • 第二章 Java入门之Java程序初体验

    第二章 Java程序初体验 2 1 Java背景介绍 2 1 1 Java名称的由来 在最初 Java语言是叫做oak 橡树 是因为刚开始公司门口有一颗橡树 所以就叫做橡树 然后发现oak已经被美国橡树公司注册了 就不能再继续使用了 所以需
  • 常用的共识机制

    1 PoW Proof of Work 工作量证明机制 基本原理 这是比特币采用的共识机制 也是最早的 理解起来 很简单 就是 按劳取酬 你付出多少劳动 工作 就会获得多少报酬 比特币等加密货币 在网络世界里 这里的劳动就是你为网络提供的计
  • 将使用idea编写的项目分享到GIT上

    1 注册账户 登录gitee 登录成功后新建一个仓库 点击创建 2 安装git Windows版 Git官网地址 https git scm com downloads下载 具体安装步骤如下 鼠标右击桌面 看到这个Git GUI Here
  • ajax 跨站返回值,跨域AJAX解决方案

    由于浏览器存在同源策略机制 同源策略阻止从一个源加载的文档或脚本获取或设置另一个源加载的文档的属性 同源策略的判定规则 如果两个页面拥有相同的协议 端口 和主机 那么这两个页面就属于同一个源 图示例 同源策略判定 png 特别的 由于同源策
  • 常用ARM汇编指令

    空指令 nop nop 传输指令 MOV MOV R1 3 用c表示 r1 3 MOV R1 0X17 r1 0x17 MOV R0 R1 r0 r1 MVN R0 R1 将RO取反给R1 相当于r1 r0 建议赋值时候直接给16进制方便调
  • Python 自定义异常处理Error函数

    usr bin env python coding utf 8 CODEMSG 2000 u True 4000 u 客户上传的文件格式不正确 4010 u 客户上传的文件格式是要求格式以外的文件 4001 u 客户上传的文件列超过1024
  • 计算机二级wps知识点,计算机二级MS office和WPS office如何备考?

    计算机二级MS office和WPS office如何备考 首先IT考试网带大家看下它们的分值是如何设置的 具体来讲 就是单项选择题 20分 含公共基础知识部分10分 文字处理题 word 30分 电子表格题 excel 30分 演示文稿题
  • 有道网页翻译chrome插件---我用过的最好的翻译插件

    大家知道很多优秀计算机的资源和文章都是用英文写的 对于英文不好的人来说 看纯英文的文章会有点吃力 而且会有恐惧感 但是不看些英文的资料 无疑是对外界精彩的思想精华 前沿技术关上了一扇门 之前用过google的网页翻译 虽然很强大 可以翻译多
  • Android13 编译错误汇总

    1 error New setting keys are not allowed 一版是在Settings中添加了新的字段导致的 解决 在你的字段上面加上 SuppressLint NoSettingsProvider 继续编译应该会出现
  • vCloud Suite Deployment---vCloud 部署

    原文 A vCloud Suite environment combines multiple VMware products and allows you to build and operate software defined dat
  • Vue9--render和ref

    render Vue渲染组件的两种形式 1 先注册组件 在Vue实例中通过标签名引用 此时不会覆盖实例控制区域 2 注册组件 在Vue实例中通过render方法渲染 此时会覆盖实例控制区域 div dddsfsfgdsfggs div
  • 在线应用的 Serverless 实践

    作者 唐慧芬 黛忻 阿里云产品专家 导读 毫无疑问 Serverless 能够在效率和成本上给用户带来巨大收益 那具体到落地又应该怎么做呢 本文就给大家详细解读 Serverless 的落地实践 Serverless 落地企业级应用的挑战
  • XML外部实体漏洞(XXE)详解与编程

    XML外部实体漏洞 XXE 详解与编程 XML外部实体漏洞 XXE 是一种常见的网络安全漏洞 它允许攻击者通过操纵XML解析器来访问和读取服务器上的文件 甚至执行远程代码 本文将详细介绍XXE漏洞的原理和常见的攻击方式 并提供相应的源代码示
  • idea实现类快捷生成接口方法

    接口类 实现类 当我们实现了接口后 并没有像eclipse那样 鼠标放上去就会提示生成接口方法 鼠标定位到UserService类后面 快捷键 Alt Enter 选择Implement methods 选中点OK就自动生成了 还有一种方法
  • 《C++ Primer》读书笔记第十六章-1-定义模板

    笔记会持续更新 有错误的地方欢迎指正 谢谢 这一章特别实用 神器 gt 模板 泛型编程能处理在编译之前类型不知道的情况 在编译时获知类型 比如我们学过的容器 迭代器和算法都是泛型编程 模板是C 中泛型编程的基础 记住 一个模板就是一个创建类