C++11中模板隐式实例化与显式实例化的定义详解分析

目录
  • 1. 隐式实例化
  • 2. 显式实例化声明与定义
  • 3. 显式实例化的用途

1. 隐式实例化

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

template<typename T>
T add(T t1, 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;//隐式实例化
}

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>就能通过编译了。

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

参考文献:Template Instantiation (Using the GNU Compiler Collection (GCC))

到此这篇关于C++11中模板隐式实例化与显式实例化的定义详解分析的文章就介绍到这了,更多相关C++模板实例化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2022-04-25

C++模板Template详解及其作用介绍

目录 1. 模板 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式 2.3 函数模板原理 2.4 函数模板的实例化 2.5 模板参数的匹配原则 2.6声明定义分离 3. 类模板 3.1 类模板格式 3.2 类模板的实例化 3.3 类模板中函数放在类外进行定义时 4. 模板分离编译 4.1 什么是分离编译 4.2 模板的分离编译 5. 缺省值与返回值 6. 总结 1. 模板 首先模板分为函数模板和类模板 想到模板,就会联想到泛型编程 泛型编程:编写与类型无关的通用代码,是代码复用的一种手

c++模板自定义数组

目录 1.自定义数组.hpp--文件 2.测试文件 前言: 制造通用模板,创建自定义的数组, 一个数组,里面有这么几个属性,数组容量,数组元素个数,数组本身内存地址,这几个数据都是定义私有类型,提供有参构造,让用户可以构造出这个数组对象.下面是有参构造和拷贝构造和析构函数还有operator=重载的代码 在前面类模板中成员函数创建有这个主意问题,最好的办法就是把类模板写在一个hpp的文件中,不要拆开写成多个文件 1.自定义数组.hpp--文件 #pragma once #include<iost

C++将模板实现放入头文件原理解析

目录 写在前面 例子 原因 分析 解决方案 方案一 方案二 参考 写在后面 写在前面 本文通过实例分析与讲解,解释了为什么C++一般将模板实现放在头文件中.这主要与C/C++的编译机制以及C++模板的实现原理相关,详情见正文.同时,本文给出了不将模板实现放在头文件中的解决方案. 例子 现有如下3个文件: // add.h template <typename T> T Add(const T &a, const T &b); // add.cpp #include "

C++超详细讲解模板的使用

目录 一.函数模板 1.1函数模板概念 1.2 函数模板格式 1.3 函数模板的原理 1.4 函数模板的实例化 二.类模板 2.1 类模板的定义格式 2.2类模板的实例化 总结 一.函数模板 1.1函数模板概念 函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本. 1.2 函数模板格式 template<typename T1, typename T2,…,typename Tn> 返回值类型 函数名(参数列表){} template<

如何C++使用模板特化功能

目录 前言: 1.函数模板的特化 2.类模板的特化 前言: 通过定义模板,使得函数或者类不依赖于特定的类型,这样大幅提升了代码的复用性. 然而,不管是类模板还是函数模板,对所有的类型都是采用相同的处理方式(同一份代码).为此,C++提出了“模板特化的概念”,对特定类型提供模板的改造. 比如,对于函数模板来说,可以通过特化指定特定的类型,针对此特定类型对函数体内容进行重写,使得函数对特定类型实现特定功能. 注意: 按照语法,对函数模板和类模板进行特化后,就变为普通函数和普通的类,而不再是模板. 1

C++模板全方位深入解读

目录 1.泛型编程 2.函数模板 概念 函数模板的格式 函数模板的原理 函数模板的实例化 隐式实例化 显式实例化 模板参数的匹配原则 3.类模板 (1) 类模板的定义格式 (2) 类模板的实例化 4.非类型模板参数 5.模板特化 (1)函数模板的特化 (2)类模板的特化 全特化 偏特化 6.模板的分离编译 问题分析 1.泛型编程 如何实现一个通用的交换函数? 这点函数重载可以做到,比如一下Swap函数的重载,分别重载了俩种不同参数类型的Swap void Swap(int& x, int&

C++可变参数模板的展开方式

文章目录 前言可变参数模板的定义参数包的展开递归函数方式展开逗号表达式展开enable_if方式展开折叠表达式展开(c++17) 总结 前言 可变参数模板(variadic templates)是C++11新增的强大的特性之一,它对模板参数进行了高度泛化,能表示0到任意个数.任意类型的参数.相比C++98/03这些类模版和函数模版中只能含固定数量模版参数的“老古董”,可变模版参数无疑是一个巨大的进步. 如果是刚接触可变参数模板可能会觉得比较抽象,使用起来会不太顺手,使用可变参数模板时通常离不开模

C++&nbsp;深入浅出探索模板

目录 非类型模板参数 模板特化 函数模板特化 类模板特化 全特化 偏特化 模板分离编译 模板的分离编译 解决方法 总结 非类型模板参数 模板参数分类类型形参与非类型形参. 类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称. 非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用. 注意: 浮点数,类对象以及字符串是不允许作为非类型模板的. 非类型的模板参数必须在编译期就能确认结果. 模板特化 有时候,编译默认函数模板

c++分离讲解模板的概念与使用

目录 泛类编程 函数模板 函数模板的概念 函数模板的使用 函数模板的实例化 函数模板的匹配原则 类模板 类模板的定义格式 类模板的实例化 泛类编程 学习模板,首先我们需要了解一下什么是泛类编程 #include<iostream> using namespace std; int add(int a, int b) { return a + b; } double add(double a, double b) //这两个add构成了函数重载 { return a + b; } int mai

比较不错的asp模板引终极讲解(WEB开发之ASP模式)

作者: 孙立 Email: 17bizAT126.com 写于:2006/3/5 版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明 http://sunli.cnblogs.com/archive/2006/03/05/343095.html 关键词:ASP 模板 摘要: 阐述一种全新的ASP模板引擎,实现代码(逻辑)层与HTML(表现)层的分离.这种模板实现方法避免了一 般ASP模板加载模板文件(加载组件)和替换所浪费的资源,实现编译型的模板引擎,提高程序的执

PHP的自定义模板引擎

前面的话 在大多数的项目组中,开发一个Web程序都会出现这样的流程:计划文档提交之后,前端工程师制作了网站的外观模型,然后把它交给后端工程师,它们使用后端代码实现程序逻辑,同时使用外观模型做成基本架构,然后工程被返回到前端工程师继续完善.就这样工程可能在后端工程师和前端工程师之间来来回回好几次.由于后端工程师不干预任何相关HTML标签,同时也不需要前端代码和后端代码混合在一起.前端工程师只需要配置文件,动态区块和其他的界面部分,不必要去接触那些错综复杂的后端代码.因此,这时候有一个很好的模板支持

[PHP]模板引擎Smarty深入浅出介绍

Smarty介绍 什么是模版引擎 不知道从什么时候开始,有人开始对 HTML 内嵌入 Server Script 觉得不太满意.然而不论是微软的 ASP 或是开放源码的 PHP,都是属于内嵌 Server Script 的网页伺服端语言.因此也就有人想到,如果能把程序应用逻辑 (或称商业应用逻辑) 与网页呈现 (Layout) 逻辑分离的话,是不是会比较好呢? 其实这个问题早就存在已久,从交互式网页开始风行时,不论是 ASP 或是 PHP 的使用者都是身兼程序开发者与视觉设计师两种身份.可是通常

深入PHP购物车模块功能分析(函数讲解,附源码)

一,购物车概述购物车是为消费者在网上购物中提供一个临时存储商品的地方.其主要功能包括:添加商品.删除商品.更改商品数量.商品金额小计.商品金额总计和清空购物车:还包括生成订单.订单打印.订单预览.提交订单和取消购物等.购物车的操作流程:首先,登录到网站中浏览商品:然后,购买指定的商品,进入购物车页面中,在该页面可以实现更改商品数量.删除商品.清空购物车.继续购物等:最后,填写收货人信息,生成订单,订单打印.预览,提交订单等操作. 二,热点关键技术1,Smarty模块的安装配置smarty是一个使

asp模板引擎终结者(WEB开发之ASP模式)

阐述一种全新的ASP模板引擎,实现代码(逻辑)层与HTML(表现)层的分离.这种模板实现方法避免了一 般ASP模板加载模板文件(加载组件)和替换所浪费的资源,实现编译型的模板引擎,提高程序的执行速度和稳定性. 内容:        当前,WEB开发已经变得非常火爆,因为各种应用,已经约来越要求表现层和逻辑层的分离.ASP和HTML夹在一起程序将变得难于维护,可读性也差.在PHP领域,模板引擎已经非常普遍,如phplib,SMARTY,等等.有使用替换方式的,也有编译方式的(SMARTY),它们都

解析Javascript单例模式概念与实例

前言 和其他编程语言一样,Javascript同样拥有着很多种设计模式,比如单例模式.代理模式.观察者模式等,熟练运用Javascript的设计模式可以使我们的代码逻辑更加清晰,并且更加易于维护和重构. 本文将介绍Javascript模式中较为常见和实用的模式--单例模式,主要分为概念和实例部分.在介绍实例的同时也会对代码中额外的知识点进行讲解. 单例模式概念 首先什么是单例模式?可以这样理解:单例模式旨在保证一个类仅有一个实例,并提供一个全局的访问点. 可能有人还是不太理解单例的概念,那么你可

PHP模板引擎SMARTY

用PHP实现MVC开发模式的逻辑层和表示层有多种模板引擎可供选择, 但是官方引擎SMARTY诞生后,选择就有了变化.它的理念和实现都是 相当"前卫"的.本文主要讨论SMARTY之于其他模板引擎的不同特点, 简要介绍了该引擎的安装及使用,并用一个小的测试案例对比了 SMARTY和PHPLIB template的速度和易用性. 一.MVC需要模板 MVC最早是在SmallTalk语言的开发过程中总结出的一种设计模式,MVC分别代 表了"模型"."视图"

浅谈ES6 模板字符串的具体使用方法

写在前面 关于 ES6, 也终于在 2015 年的 7 月 18 号尘埃落定了.虽然说各大浏览器还没有全面的支持,不过这并不妨碍我们一颗想要撸一把的心.在后端,可以使用 Node.js(0.12+)或 io.js, 前端的话,也可以使用Babel 或Traceur 进行语法预转义成 ES5使用 . 关于该系列(不知道能不能成为一个系列,总是各种懒),会没有规律的挑选一些内容来学习.欢迎大家积极纠错,留言探讨. 模板字符串(template strings) 用法 // 普通字符串 `In Jav

常用的javascript设计模式

阅读目录 什么是设计模式 单体模式: 工厂模式: 单例模式 观察者模式(发布订阅模式) 策略模式 模板模式 代理模式 外观模式 设计模式太多了,貌似有23种,其实我们在平时的工作中没有必要特意去用什么样的设计模式,或者你在不经意间就已经用了设计模式当中的一种.本文旨在总结平时相对来说用的比较多的设计模式. 什么是设计模式 百度百科: 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结. 使用设计模式是为了可重用代码.让代码更容易被他人理解.

linux多线程编程详解教程(线程通过信号量实现通信代码)

线程分类 线程按照其调度者可以分为用户级线程和核心级线程两种. (1)用户级线程 用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要特定的内核支持.在这里,操作系统往往会提供一个用户空间的线程库,该线程库提供了线程的创建.调度.撤销等功能,而内核仍然仅对进程进行管理.如果一个进程中的某一个线程调用了一个阻塞的系统调用,那么该进程包括该进程中的其他所有线程也同时被阻塞.这种用户级线程的主要缺点是在一个进程中的多个线程的调度中无法发挥多处理器的优势.