模板类、模板函数的模板类型显式实例化及其用途(转载)

2023-11-17

转载自:C++11模板隐式实例化、显式实例化声明、定义(简单易懂)_云飞扬_Dylan的博客-CSDN博客_模板隐式实例化

1. 隐式实例化
在代码中实际使用模板类构造对象或者调用模板函数时,编译器会根据调用者传给模板的实参进行模板类型推导然后对模板进行实例化,此过程中的实例化即是隐式实例化。

注:个人理解:模板类、模板函数的模板类型也是对应类、函数的“类型参数”,调用模板类、模板函数时将根据传入的“模板类型参数”才能实例化完整的类、完整的函数,不同的“模板类型参数”能实例化出不同的类、函数。

template<typename T>
T add(T t1, T t2)
{
    return t1 + t2;
}
 
template<typename T>
class Dylan
{
public:
    T m_data;
};
 
int main()
{
    int ret = add(3,4);//隐式实例化,int add<int>(int t1, int t2);
    Dylan<double> dylan;//隐式实例化
}

注:个人理解:隐式实例化模板函数可以不用add<int>(3,4)而直接add(3,4),编译器会根据调用者传给模板的实参进行“模板类型参数”推导然后对模板进行实例化,而实例化模板类总是要给出模板类型的。
2. 显式实例化声明、定义
extern template int add<int>(int t1, int t2);//显式实例化声明
extern template class Dylan<int>;            //显式实例化声明
 
template int add<int>(int t1, int t2);       //显式实例化定义
template class Dylan<int>;                   //显式实例化定义
当编译器遇到显式实例化声明时,表示实例化定义在程序的其他地方(相对于当前cpp文件)即在其他某一个cpp文件中定义,因此不再按照模板进行类型推导去生成隐式实例化定义。

当编译器遇到显式实例化定义时,根据定义所提供的模板实参去实例化模板,生成针对该模板实参的实例化定义。

3. 显式实例化的用途
模板类、函数通常定义在头文件中,这些头文件会被很多cpp文件包含,在这些cpp文件中会多次使用这些模板,比如下面的例子:

//template.hpp
template<typename T>
class Dylan
{
public:
    T m_data;
};
 
//test1.cpp
#include "template.hpp"
Dylan<int> t1;
Dylan<int> t2;
 
//test2.cpp
#include "template.hpp"
Dylan<int> t3;
Dylan<int> t4;
在test1.cpp/test2.cpp 中多次实例化了Dylan<int>类,按说编译完后的可执行程序中会包含多份Dylan<T>的定义,然而实际上,整个程序中却只有一份Dylan<T>的定义。这个处理是在编译和链接过程中实现的,目前主流的实现模式有两种:      

a. Borland模式

Borland模式通过在编译器中加入与公共块等效的代码来解决模板实例化问题。在编译时,每个文件独立编译,遇到模板或者模板的实例化都不加选择地直接编译。在链接的时候将所有目标文件中的模板定义和实例化都收集起来,根据需要只保留一个。这种方法实现简单,但因为模板代码被重复编译,增加了编译时间。在这种模式下,我们编写代码应该尽量让模板的所有定义都放入头文件中,以确保模板能够被顺利地实例化。要支持此模式,编译器厂商必须更换支持此模式的链接器。

b. Cfront模式

AT&T编译器支持此模式,每个文件编译时,如果遇到模板定义和实例化都不直接编译,而是将其存储在模板存储库中(template repository)。模板存储库是一个自动维护的存储模板实例的地方。在链接时,链接器再根据实际需要编译出模板的实例化代码。这种方法效率高,但实现复杂。在这种模式下,我们应该尽量将非内联成员模板的定义分离到一个单独的文件中,进行单独编译。

在一个链接器支持Borland模式的编译目标(编译后的可执行文件)上,g++使用Borland模式解决实例化问题。比如ELF(Linux/GNU), Mac OS X, Microsoft windows, 否则,g++不支持上述两种模式。

如何避免Borland模式的缺点?

上面我们说g++实现的是Borland 模式,由于我们为每一份实例化生成代码,这样在大型程序中就有可能包含很多重复的实例化定义代码,虽然链接阶段,链接器会剔除这些重复的定义,但仍然会导致编译过程中的目标文件(或者共享库文件)过于庞大。这时候,我们就可以通过C++11的模板显式实例化的方法解决。看下面的代码:

// template.hpp
template<typename T>
class Dylan
{
public:
    Dylan(T t);
    T m_data;
};
 
// template.cpp
#include "template.hpp"
template<typename T>
Dylan<T>::Dylan(T t)
{
    m_data = t;
}
 
template class Dylan<int>; //模板实例化定义
 
// main.cpp
#include "template.hpp"
extern template class Dylan<int>; //模板实例化声明,告诉编译器,此实例化在其他文件中定义
                                  //不需要根据模板定义生成实例化代码  
int main()
{
    Dylan<int> dylan(3);//OK, 使用在template.cpp中的定义
    Dylan<float> dylan1(3.0);//error, 引发未定义错误
}
   上面的代码中,我们将模板类的具体定义放在template.cpp中,并且在template.cpp中通过显式实例化定义语句具体化了Dylan<int>。在main.cpp中,我们通过显式实例化声明告诉编译器,Dylan<int>将在其他文件中定义,不需要在本文件中根据template.hpp的类模板实例化Dylan<int>。

由于我们没有针对Dylan<float>做显式实例化的声明和定义,因此Dylan<float> dylan(3.0)会根据template.hpp中的类模板定义进行隐式实例化,然而构造函数是在template.cpp文件中定义的,在template.hpp中找不到构造函数的定义,因而报错。如果把构造函数的定义挪回template.hpp,那Dylan<float>就能通过编译了。

注:补充一点,在一个cpp中extern声明的变量、函数将在链接阶段前往其他cpp进行查找链接,在一个cpp中调用classA.h中声明的方法,在链接阶段也会前往classA.cpp中查找链接而不用在classA.h中extern声明,这是链接发生的两个情形;

Note:在编译中,如果指定-fno-implicit-templates,编译器就会禁止隐式实例化,从而只使用显式实例化。

参考文献:Template Instantiation (Using the GNU Compiler Collection (GCC))
————————————————
版权声明:本文为CSDN博主「云飞扬_Dylan」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Jxianxu/article/details/124359007

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

模板类、模板函数的模板类型显式实例化及其用途(转载) 的相关文章

随机推荐

  • SmartImage图片第三方控件android

    1 新建 SmartImageDemo app src main res xml network security config xml 该配置用于HTTP请求
  • 用Arduino读取MLX90393三轴霍尔传感器

    一 模块简介 Melexis MLX90393是一个三轴霍尔传感器 可以检测各个方向的磁场强度 MCU通过I2C或SPI读取X Y Z三轴磁场分量 MLX90393的数据手册已上传 https download csdn net downl
  • 【华为OD机试c++/java/python】微服务的集成测试【2023 Q1 A卷

    华为OD机试 题目列表 2023Q1 点这里 2023华为OD机试 刷题指南 点这里 题目描述 现在有n个容器服务 服务的启动可能有一定的依赖性 有些服务启动没有依赖 其次服务自身启动加载会消耗些时间 给你一个nxn 的二维矩阵 useTi
  • element的遮罩层v-loading,隐藏上面的文字和图标,添加自定义内容

    element的遮罩层v loading 隐藏上面的文字和图标 添加自定义内容 之前有个需求是想要一个遮罩层 项目组件用的是element 想用v loading 但是上面的图标和文字格式不是我想要的 效果图 思路如下 1 隐藏自带的文字和
  • go语言的iota是什么意思_Go语言学习——三分钟看透iota

    Go语言学习 三分钟看透iota 源起枚举 最近做需求时 有一个需要枚举的场景 大概有10 个枚举类型 不愿意像定义一个开关那样敷衍的写成 const SwitchOff 0 SwitchOn 1 显得不够精致 于是想到了iota 深入了解
  • 基于Keras的卷积神经网络手写数字识别

    coding utf 8 Created on Sun Aug 1 16 22 47 2021 author Yang Hongyun 构建卷积神经网络模型代码 import tensorflow as tf import keras ke
  • 使用Keras进行LSTM实战

    0 前言 在上文中进行ARIMA时序预测后 了解到强大的LSTM在处理时序预测有更为优秀的表现 因此对LSTM进行了学习 LSTM是一种时间递归神经网络 它出现的原因是为了解决RNN的一个致命的缺陷 原生的RNN会遇到一个很大的问题 叫做T
  • Redis主从集群

    Redis主从集群 1 集群结构 我们搭建的主从集群结构如图 共包含三个节点 一个主节点 两个从节点 这里我们会在同一台虚拟机中开启3个redis实例 模拟主从集群 信息如下 IP PORT 角色 192 168 150 101 7001
  • 文件上传 拿 shell

    启动docker开启镜像环境 成功在主机上访问到 根据弱密码tomcat tomcat登陆管理后台 在下面位置可以找到上传点 上传对应的war文件 先把一剑jsp马保存到zip压缩包中 再修改后缀名为war上传 可以看到上传成功 连接成功
  • 获取表单中某个元素,返回数组

    获取id为editForm的表中中所有input元素的对象 input editForm 获取每个input中name和val var ret input editForm each function var nm this attr na
  • OpenWrt后台管理启用https-OpenSSL

    OpenWrt 默认使用http 访问管理后台 这样不安全 推荐修改为 https 访问 加密数据传输 本文介绍配置步骤 1 卸载旧的ssl软件包 root OpenWrt opkg remove luci ssl px5g px5g mb
  • 深入《C++ Core Guidelines解析》:提升C++编程实践的关键指南

    目录 1 写在前面 2 推荐理由 3 内容介绍 4 作者介绍 5 赠书 or 购买 1 写在前面 C Core Guidelines是一个正在进行的开源项目 通过将广泛认可的现代C 上佳实践集中在一个地方来解决这些问题 Core Guide
  • Kylin--简介及图解架构

    Kylin简介 Kylin 麒麟 的诞生背景 Kylin的应用场景 为什么要使用Kylin Kylin的总体架构 Kylin 麒麟 的诞生背景 ebay 中国团队研发的 是第一个真正由中国人自己主导 从零开始 自主研发 并成为Apache顶
  • Linux中创建文件与文件夹

    一 创建文件夹 命令 mkdir 文件夹名 例 一开始home目录下没有test文件夹 命令创建后生成 二 创建文件 命令 touch 文件名 例 一开始test文件夹下没有boot properties 命令创建后生成 三 注意事项 创建
  • linux下挂载移动硬盘(ntfs格式),Linux下挂载移动硬盘(NTFS格式)

    工作中遇到linux系统 Red Hat Enterprise5 7 挂载希捷ntfs格式移动硬盘 会跳出一个ERROR提示框 The volume EAGET NQH user the ntfs file system which is
  • arcpy导入报错 “ImportRrror: No module named arcpy”

    在使用ArcGIS自带的Python IDLE处理数据的时候 导入arcpy报错 ImportError No module named arcpy 我遍历了各解决方法依然无法成功导入arcpy 后经过查询 探索 通过如下方法得以成功解决
  • aoj1303

    继续python系列 python能够自动推断类型这个太好用了 根本不用声明类型 自己根据运行情况推断出所用的类型 所以在定义函数的时候根本不用声明参数的类型 下面这个题目aoj1303 求2的指数 如下 def gethex a li w
  • 关于飞书的告警通知,这里有个更好的办法

    飞书 是字节跳动于2016年自研的新一代一站式协作平台 是保障字节跳动全球五万人高效协作的办公工具 飞书将即时沟通 日历 云文档 云盘和工作台深度整合 通过开放兼容的平台 让成员在一处即可实现高效的沟通和流畅的协作 全方位提升企业效率 20
  • Git 使用

    Git 一 Git基础 1 Git介绍 Git是目前世界上最先进的分布式版本控制系统 2 Git与Github 2 1 两者区别 Git是一个分布式版本控制系统 简单的说其就是一个软件 用于记录一个或若干文件内容变化 以便将来查阅特定版本修
  • 模板类、模板函数的模板类型显式实例化及其用途(转载)

    转载自 C 11模板隐式实例化 显式实例化声明 定义 简单易懂 云飞扬 Dylan的博客 CSDN博客 模板隐式实例化 1 隐式实例化 在代码中实际使用模板类构造对象或者调用模板函数时 编译器会根据调用者传给模板的实参进行模板类型推导然后对