服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C++11中模板隐式实例化与显式实例化的定义详解分析

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

2022-11-16 15:52云飞扬_Dylan C/C++

实例化是为在程序中的函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成函数定义时,得到的是模板实例。这即是函数模板的实例化。而函数模板实例化又分为两种类型:隐

1. 隐式实例化

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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. 显式实例化声明与定义

?
1
2
3
4
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文件中会多次使用这些模板,比如下面的例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//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的模板显式实例化的方法解决。看下面的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 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++模板实例化内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/Jxianxu/article/details/124359007

延伸 · 阅读

精彩推荐
  • C/C++C++ 模版双向链表的实现详解

    C++ 模版双向链表的实现详解

    本篇文章是对C++中的模版双向链表进行了详细的分析介绍,需要的朋友参考下...

    C++教程网4192020-12-14
  • C/C++C语言数据结构之动态分配实现串

    C语言数据结构之动态分配实现串

    这篇文章主要介绍了C语言数据结构之动态分配实现串的相关资料,希望通过本文能帮助到大家,让大家实现数据结构中动态分配实现串的实例,需要的朋友...

    llcomeon1_5582021-06-08
  • C/C++C++编程中队内联函数的理解和使用

    C++编程中队内联函数的理解和使用

    这篇文章主要介绍了C++编程中队内联函数的理解和使用,简单举例讲解了inline关键字引出的内联函数的相关知识,需要的朋友可以参考下...

    coder_xia6112021-03-22
  • C/C++深入VC回调函数的使用详解

    深入VC回调函数的使用详解

    本篇文章是对VC回调函数的使用进行了详细的分析介绍,需要的朋友参考下...

    C语言教程网5062020-12-02
  • C/C++C++字符串类的封装你真的了解吗

    C++字符串类的封装你真的了解吗

    这篇文章主要为大家详细介绍了C++字符串类的封装,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你...

    是小明同学啊11412022-09-22
  • C/C++MFC模拟实现自定义消息发送

    MFC模拟实现自定义消息发送

    在MFC框架下,有很多系统已经定义好的消息,例如ON_WM_LBUTTONDOWN()、ON_WM_MBUTTONDOWN()等等。本文将利用这些定义好的消息模拟实现一下消息发送,需要的可以...

    中国好公民st8652022-09-07
  • C/C++错误:sem_union的存储大小未知问题的解决方法

    错误:sem_union的存储大小未知问题的解决方法

    这篇文章主要介绍了错误:sem_union的存储大小未知问题的解决方法,需要的朋友可以参考下...

    C语言教程网9892021-04-18
  • C/C++C语言查找数组里数字重复次数的方法

    C语言查找数组里数字重复次数的方法

    这篇文章主要介绍了C语言查找数组里数字重复次数的方法,涉及C语言针对数组的遍历与判断技巧,具有一定参考借鉴价值,需要的朋友可以参考下...

    宋勇野4172021-03-03