C++模板背后的黑箱操作:编译器

C++模板背后的黑箱操作:编译器

编译器如何处理模板

模板代码的处理

为了理解模板的复杂性,你需要了解编译器是如何处理模板代码的。当编译器遇到模板方法定义时,它会进行语法检查,但实际上不会编译模板。编译器不能编译模板定义,因为它不知道这些模板将用于哪些类型。编译器不可能为像 x = y 这样的代码生成代码,而不知道 xy 的类型。

当编译器遇到模板的实例化,例如 Grid<int>,它会通过将类模板定义中的每个 T 替换为 int 来为 int 版本的 Grid 模板编写代码。当编译器遇到模板的不同实例化,例如 Grid<SpreadsheetCell>,它会为 SpreadsheetCell 编写另一个版本的 Grid 类。编译器只是写出了如果没有模板支持,你需要为每种元素类型编写单独类时的代码。这里没有魔法;模板只是自动化了一个烦人的过程。如果你在程序中没有为任何类型实例化类模板,那么类方法定义就永远不会被编译。

这种实例化过程解释了为什么你需要在定义的各个地方使用 Grid<T> 语法。当编译器为特定类型(如 int)实例化模板时,它会将 T 替换为 int,使 Grid<int> 成为该类型。

选择性实例化

对于隐式类模板实例化,如以下示例:

Grid<int> myIntGrid;

编译器总是为类模板的所有虚拟方法生成代码。然而,对于非虚拟方法,编译器只为你实际调用的那些非虚拟方法生成代码。例如,给定前面的 Grid 类模板,假设你在 main() 中写了这样的代码(仅此代码):

Grid<int> myIntGrid;
myIntGrid.at(0, 0) = 10;

编译器仅为 int 版本的 Grid 生成无参数构造函数、析构函数和非 constat() 方法。它不会生成其他方法,如拷贝构造函数、赋值运算符或 getHeight()。这被称为选择性实例化。

存在的风险是,某些类模板方法中的编译错误可能会被忽略。未使用的类模板方法可能包含语法错误,因为这些不会被编译。这使得测试所有代码的语法错误变得困难。

你可以通过使用显式模板实例化来强制编译器为所有方法(虚拟和非虚拟)生成代码。以下是一个示例:

template class Grid<int>;

注意:显式模板实例化有助于发现错误,因为它强制编译器编译所有即使未使用的类模板方法。使用显式模板实例化时,不要只尝试使用基本类型(如 int)实例化类模板,还要尝试使用更复杂的类型(如 string)。

模板对类型的要求

类型独立的代码编写

当你编写与类型无关的代码时,必须对这些类型做出某些假设。例如,在 Grid 类模板中,你假设元素类型(由 T 表示)是可销毁的、可拷贝/移动构造的,以及可拷贝/移动赋值的。

当编译器尝试用不支持类模板方法所使用的所有操作的类型来实例化模板时,代码将无法编译,且错误消息通常相当晦涩难懂。

然而,即使你想使用的类型不支持类模板的所有方法所需的操作,你也可以利用选择性实例化来使用某些方法而不是其他方法。

C++20 引入的概念(Concepts)

C++20 引入了概念(concepts),允许你为模板参数编写编译器可以解释和验证的要求。如果传递给模板实例化的模板参数不满足这些要求,编译器可以生成更易读的错误消息。后面将讨论概念。

概念为模板编程增加了额外的类型安全性,它通过为模板参数提供一个明确的接口合约来实现。这种方式不仅可以防止类型不匹配的问题,还可以改善模板错误消息的可读性,从而使模板代码更容易维护和理解。

类模板代码的文件

在类模板中,类模板定义和方法定义必须对任何使用它们的源文件可用。有几种机制可以实现这一点:

方法定义与类模板定义在同一文件

你可以将方法定义直接放在定义类模板本身的模块接口文件中。当你在另一个源文件中导入这个模块以使用模板时,编译器将能够访问它所需的所有代码。这种机制用于之前的 Grid 实现。

方法定义在单独的文件

或者,你可以将类模板方法定义放在一个单独的模块接口分区文件中。然后,你还需要将类模板定义放在自己的分区中。例如,Grid 类模板的主模块接口文件可能如下所示:

export module grid;
export import :definition;
export import :implementation;

这导入并导出了两个模块分区:定义(definition)和实现(implementation)。类模板定义在定义分区中定义:

export module grid:definition;
import <vector>;
import <optional>;
export template <typename T> class Grid { ... };

方法的实现位于实现分区中,该分区还需要导入定义分区,因为它需要 Grid 类模板定义:

export module grid:implementation;
import :definition;
import <vector>;
...
export template <typename T> Grid<T>::Grid(size_t width, size_t height): m_width { width }, m_height { height } { ... }

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

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

相关文章

Python Opencv实践 - 简单的AR项目

这个简单的AR项目效果是&#xff0c;通过给定一张静态图片作为要视频中要替换的目标物品&#xff0c;当在视频中检测到图片中的物体时&#xff0c;通过单应矩阵做投影&#xff0c;将视频中的物体替换成一段视频播放。这个项目的所有素材来自自己的手机拍的视频。 静态图片&…

Java注册并监听全局快捷键

背景 之前在博客中分享了SWT托盘功能, 随之带来一个问题, 当程序最小化后无法快速唤醒, 按照平时使用软件的思路, 自然想到了注册全局快捷键, 本文介绍使用java方式实现全局快捷键的注册. 方案 通过google,搜到一个现成的库: jintellitype, 使用maven可以直接引用, 非常方便…

istio为什么能代替传统的SpringCloud 服务网格Istio概述

服务网格Istio概述 什么是服务网格(Service Mesh)&#xff1f;istio简介边车模式&#xff08;Sidecar&#xff09;为什么istio能代替传统SpringCloud&#xff1f;整体架构 首先奉上 istio官网 什么是服务网格(Service Mesh)&#xff1f; 服务网格详解 服务网格&#xff08;Se…

JS - 包装类型

目录 1&#xff0c;什么是包装类型2&#xff0c;作用3&#xff0c;和其他引用类型的区别4&#xff0c;如何为基本类型添加属性或方法 1&#xff0c;什么是包装类型 在 js 中&#xff0c;数据类型分为基本数据类型和引用数据类型。 而这2个类型有个明显的区别&#xff1a;引用…

SpringMVC修炼之旅(2)基础入门

一、第一个程序 1.1环境配置 略 1.2代码实现 package com.itheima.controller;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;//定义…

【JaveSE】:认识异常

异常 一.异常的概念二.异常的体系结构1.基础结构2.异常分类 三.异常的处理1.防御式编程2.异常的抛出3.异常捕获4.try-catch捕获并处理5.finally 四.异常处理流程五.自定义异常 一.异常的概念 在Java中&#xff0c;将程序执行过程中发生的不正常行为称为异常。 算术异常 数组越界…

推荐一款手持式ECU刷写设备,国产软件配合使用

同星智能开发的一款手持式ECU刷写工具——TF1011&#xff0c;在TSMaster中导入诊断流程即可离线一键刷写。在 PC 端完成配置后&#xff0c;在设备可以在手持式离线场景下实现&#xff1a;基于 UDS 协议的诊断和基于 UDS 协议的 Flash Bootloader 程序更新功能。 产品简介—TF10…

Spring 面试题——事务

目录 1.谈谈对 Spring 事务的理解。2.Spring 管理事务的方式有哪几种&#xff1f;3.✨Spring 事务底层源码是如何实现的&#xff1f;3.1.后置处理3.2.事务执行3.3.总结 4.Transactional 注解有什么作用&#xff1f;它的常用属性有哪些&#xff1f;5.✨Spring 事务中的传播行为是…

ansible常用模块介绍

ansible运行模块的两种方式 Ad - Hoc 利用 ansible 命令直接完成管理 &#xff0c; 主要用于临时命令使用场景 ansible westos -m shell -a ls /mnt playbook ansible 脚本 &#xff0c; 主要用于大型项目场景 &#xff0c; 需要前期的规划 vim test.yml - hosts: all task…

【栈】车队

题目&#xff1a; /** 单调栈&#xff1a;存储到终点的时间&#xff0c;需要考虑浮点数* 思路&#xff1a;首先按照距离进行排序&#xff08;目的&#xff1a;如果离终点远的车辆用时比前面的车辆用时短&#xff0c;则是一个车队)* 排序后计算每一辆车辆的时间如果用…

web:[SUCTF 2019]CheckIn(一句话木马,.user.ini)

题目 页面显示 上传文件&#xff0c;随便上传一个文件试试 上传了一个文本&#xff0c;显示失败&#xff0c;不是图片 那就换图片马上传试试 不能包含<?,换一种写法&#xff0c;需要加上GIF89a&#xff0c;进行exif_imagetype绕过 上传成功 这里用.user.ini或者用post传参…

甘草书店:#8 2023年11月22日 星期三「“说一套做一套”的甘草与麦田」

最近与甘草书店的投资方和意向投资方沟通&#xff0c;听取了来自不同领域不同人群的观点。他们讲的都有道理&#xff0c;但他们说的都不是甘草。就像“麦田”成立之前&#xff0c;世间没有“麦田”一样&#xff1b;“甘草”出现之前&#xff0c;世间没有也没有“甘草”。 故事…

力扣116. 填充每个节点的下一个右侧节点指针(详细讲解root根节点的理解)

题目&#xff1a; 给定一个 完美二叉树 &#xff0c;其所有叶子节点都在同一层&#xff0c;每个父节点都有两个子节点。二叉树定义如下&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右…

220V工频正弦波逆变器设计

摘 要 与传统逆变器相比&#xff0c;工频正弦波逆变器具有电容电压应力低的优点。但是工频正弦波逆变器存在开关器件电压应力大和开关频率高的缺陷。将SPWM调制策略应用于工频正弦波逆变器&#xff0c;并在不产生电流纹波的条件下实现了最大调制度&#xff0c;不仅能够减小开关…

05.开闭原则(Open Closed Principle)

“你这个人怎么这么轴&#xff1f;让你改改以前的代码怎么和要了你命似的&#xff1f;难道你的能力仅限于此吗&#xff1f;” “你懂什么&#xff1f;我有我的原则&#xff01;我有我的信仰&#xff01;” 一言 开闭原则即&#xff1a;对扩展开放&#xff0c;对修改关闭&#…

D. In Love

贪心&#xff0c;维护最靠左的右端点以及最靠右的左端点 // Problem: D. In Love // Contest: Codeforces - Codeforces Round 905 (Div. 3) // URL: https://codeforces.com/contest/1883/problem/D // Memory Limit: 256 MB // Time Limit: 2000 ms // // Powered by CP Edi…

【从0配置JAVA项目相关环境1】jdk + VSCode运行java + mysql + Navicat + 数据库本地化 + 启动java项目

从0配置JAVA项目相关环境 写在最前面一、安装Java的jdk环境1. 下载jdk2. 配置jdk3. 配置环境变量 二、在vscode中配置java运行环境1. 下载VSCode2. 下载并运行「Java Extension Pack」 三、安装mysql1.官网下载MySQL2.开始安装如果没有跳过安装成功 3.配置MySQL Server4.环境变…

【爬虫】自动下载指定网站全部图片(Java版)

爬虫是一种自动化程序&#xff0c;能够模拟人类的浏览行为&#xff0c;访问网络资源并提取所需数据。它可以通过发送HTTP请求获取网页内容&#xff0c;并对网页进行解析和数据提取。 在大多数时候&#xff0c;提到爬虫我们就会想到 Python&#xff0c;其实 Java 也是可以实现爬…

判断完数(写出部分函数)

例如&#xff1a;本题要求实现一个函数&#xff0c;判断一个自然数是否是完数。如果一个自然数除自身之外的因子和等于它自己&#xff0c;则称该数为完数。例如 6 1 2 3&#xff1b;则6是完数。 函数接口定义&#xff1a; 在这里描述函数接口。&#xff1a;int isPerfect (…

ApplicationContextAware 类

优质博文&#xff1a;IT-BLOG-CN 需求&#xff1a; 使用autowired注入一些对象&#xff0c;但发现不可以直接使用Autowired&#xff0c;因为方法是static的&#xff0c;要使用该方法当前对象也必须是static&#xff0c;正常情况下Autowired无法注入静态的bean&#xff0c;于是…