【C++】关于C++模板的分离编译问题
创始人
2024-05-31 04:44:17
0

文章目录

  • 1.阐述模板的实例化和重复定义问题
  • 2.分离编译可能出现的问题
  • 3.解决方法
    • 将函数模板的定义放到头文件中
    • 模板定义的位置显式实例化
  • 模板总结


1.阐述模板的实例化和重复定义问题

C++模板是一种非常强大的工具,可以为我们提供通用的代码实现方式。然鹅,在使用模板时会涉及到模板的实例化和重复定义的问题。为了避免这些问题并提高编译效率,C++提供了模板分离编译的机制。

1.模板为什么会涉及到实例化和重复定义的问题?

C++的模板时一种通用的代码实现方式,可以根据不同的类型参数生成具体的代码实例。当程序使用一个模板时,编译器将根据其具体的类型参数生成对应的代码实例,这个过程称为模板的实例化。

在模板实例化时,编译器会根据模板定义生成对应的函数或类,并在程序中调用或实例化这些函数或类。然鹅,由于模板的定义通常都放在头文件中,当多个源文件包含相同的头文件时,就会出现重复定义的问题

以下是一个简单的示例代码,演示了在两个源文件中包含相同的头文件时,引起的重复定义错误:且该头文件中定义了一个函数模板:

// add.h
template
T add(T a, T b) {return a + b;
}
// main1.cpp
#include "add.h"int main() {int a = 1, b = 2;int c = add(a, b);return 0;
}
// main2.cpp
#include "add.h"int main() {double a = 1.5, b = 2.5;double c = add(a, b);  // error: redefinition of 'add'return 0;
}

在上述示例代码中,我们定义了一个名为add的函数模板,并在两个不同的源文件中分别包含相同的头文件add.h。当编译器在对这两个源文件进行编译时,它会对同一个函数模板进行多次实例化,从而导致重复定义错误。

error: redefinition of 'add'

2.但是它们实例化出的是两个不同类型的add函数呀,为什么有重复定义问题呢?

是的,没错。实例化出来的两个add函数,一个是int类型的,另一个是double类型的。但是这并不是导致重复定义问题的根本原因。

在C++中,函数模板的定义通常都放在头文件中,而头文件可能被多个源文件包含,当多个源文件包含相同的头文件时,其中的函数模板定义也会被多次包含,从而引发重定义问题。

具体地说,在上述实例中,编译器会对头文件add.h进行两次编译,并生成两个不同的目标文件。然后,编译器试图将两个目标文件链接到一起时,就会发现它们之间存在重复定义的符号,从而导致连接错误重复定义的符号指的是在多个目标文件中都存在,名称相同但实体不同的符号。在C++中,符号通常是函数名,变量名,类名

在上述示例中,我们定义了一个名为add的函数模板,在两个不同的源文件对其进行了示例化。当编译器将这两个源文件编译成目标文件时,它们分别包含了一个名为add这个符号打不出来,见谅)的符号。然鹅,当我们试图将这两个目标文件链接到一起时,就会发现这两个符号名称相同,但实体不同,所以会导致链接错误

2.分离编译可能出现的问题

开头说过,在使用模板时会涉及到模板的实例化和重复定义的问题。为了避免这些问题并提高编译效率,C++提供了模板分离编译的机制。

1.什么是分离编译模式?

一个项目由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译

2.模板的分离编译可能会出现的问题(分析)

在C++中,模板分离编译是指将模板的声明和定义分开存放在不同的文件中,以避免多个源文件中重复定义同一个模板的问题。然而,在实践中,模板分离编译常常会带来一些问题,,主要包括以下几个方面:

  1. 链接错误:由于模板被分成了多个文件,如果链接时遗漏了某个模板的定义,就会导致链接错误。
  2. 多次实例化:由于模板的定义通常都写在头文件中,如果多个源文件包含相同的头文件,就可能导致同一个模板被多次实例化,从而增加编译时间和代码大小
  3. 可读性差:模板分离编译会导致模板的声明和定义分散在不同的文件中,使得代码的可读性变差。

我们都知道,程序运行起来一般要以下4个步骤:

  1. 预处理:头文件展开,去注释,宏替换,条件编译等。
  2. 编译:检查代码的规范性,是否有语法错误等,确定代码实际要做的工作,在检查无误后,将代码翻译成汇编语言。
  3. 汇编:把编译阶段生成的文件转成目标文件obj
  4. 链接:将生成的各个目标文件进行链接,生成可执行文件。

多次实例化的问题,我们在前面已经讲解的很清楚了,我们现在来认识一下在模板的分离编译中,可能会遇到的链接问题

在C++程序设计中,在一个源文件中定义某个函数,然后在另一个源文件中使用该函数,这是一种非常普遍的做法。但是,如果定义和调用一个函数模板时也采用这种方式,会发生编译错误。下面的程序由三个文件组成:add.h用来对函数模板进行申明,add.cpp用来定义函数模板,main.cpp包含add.h头文件并调用相应的函数模板。

// a.h
template
T Add(const T& left, const T& right);// a.cpp
template
T Add(const T& left, const T& right)
{
return left + right;
}// main.cpp
#include"a.h"
int main()
{
Add(1, 2);
Add(1.0, 2.0);
return 0;
}

这是一个结构非常清晰的程序,但是它不能通过编译。在VS2017下的出错信息是:

在这里插入图片描述

这是很典型的链接问题,那么原因就出在分离编译的模式上。在分离编译模式下,a.cpp会生成一个目标文件a.obj,由于在a,cpp文件中,并没有发生函数模板调用,所以不会把函数模板示例化成int或double类型,那么在a.obj中就找不到模板函数的实现代码,所以在链接时就会出现错误。

在main.cpp中,虽然函数模板被调用,但是由于没有模板代码,也不能将其实例化。在main.obj中找不到模板函数int add(…),在链接时就会出现函数未定义的错误。

3.解决方法

将函数模板的定义放到头文件中

将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的。推荐使用这种。这样的话,只要包含了这个头文件,就会把函数模板的代码包含进来,若发生函数调用,可以直接依据类型进行实例化。这种方法比较推荐,但是也有不足之处。

  1. 将函数定义写在头文件中,暴露了函数的实现细节。
  2. 不符合分离编译模式的规则。

模板定义的位置显式实例化

解决代码如下:

//Add.h
#include
using namespace std;
//函数模板声明
template
T Add(const T& x,const T& y);//Add.cpp
template
T Add(const T& x,const T& y)
{return x+y;
}
//显示实例化
template int Add(const int& x,const int& y);
template double Add(const double& x,const double& y);//main.cpp
#include"Add.h"
int main()
{//调用函数模板实例化的函数cout<

上述代码,在Add.cpp文件中对函数模板进行显示实例化,这样函数模板就可以生成对应的函数,这样再链接时就不会出错了。

除了这两种方法,还有其它方法,如:
1.使用模板库:通过将模板定义封装到库文件中,可以避免多个源文件中对同一模板的重复定义,并提高代码重用性。
2.模板导出(export):在模板声明时使用export关键字,可以告诉编译器只生成一个模板示例,避免多次实例化统一模板。
3.内联函数:将模板函数定义为内联函数可以避免链接错误,同时减少函数调用的开销。

模板总结

【优点】

  1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库STL因此而产生。
  2. 增强了代码的灵活性。

【缺陷】

  1. 模板会导致代码膨胀问题,也会导致编译时间变长。
  2. 出现模板编译错误时,错误信息非常凌乱,不易定位。

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...