头图

一种将函数模板定义和声明分开的方法

  在 C++ 中为了操作简洁引入了函数模板。所谓的函数模板实际上是建立一个通用函数,其函数类型或形参类型不具体指定,用一个虚拟的类型来表达,这个通用函数就称为函数模板。

1、通用的写法

  函数模板不是一个具体的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。为了容易使用,一般通用的写法都是在头文件中直接定义函数模板,定义的同时也是声明该函数,供给其它文件包含调用。

//------fun.h或fun.hpp------//
#ifndef _FUN_H_
#define _FUN_H_

using namespace std;

template<typename T>  
void fun(int b, T c, T d) //定义函数模板 
{
  ......
}

#endif

  对编译器而言,定义函数模板的时候,编译器并不会对它进行编译,因为它没有一个实体可用,编译器只看到了声明,只有模板被实例化后(用在特定的类型上),编译器才会根据具体的类型对模板进行编译。因此当在别的文件中调用该函数模板时,根据传递的实际参数决定其功能,这样编译器就可以在编译期间看到模板函数的定义并完成模板的实例化,如果在编译的时候,找不到模板函数的定义,就先不在这一次编译中实例化该模板函数。

2、问题的引出

  但是头文件中定义和使用函数模板时,碰到了一个这样的场景,即在函数模板中使用到了全局变量:

//------fun.h或fun.hpp------//
#ifndef _FUN_H_
#define _FUN_H_

using namespace std;
int a; //定义全局变量
template<typename T>  
void fun(int b, T c, T d) //定义函数模板 
{
  ......
  a = b;
}

#endif

  因此碰到其它多个文件需要使用该函数模板时,都需要各自包含该函数模板的头文件,编译时就会出现“全局变量重复定义”的错误。

  尝试按照普通函数定义和声明分开的思路将函数模板的定义和声明分开:

  源文件:

//------fun.cpp------//   //错误做法
using namespace std;
int a; //定义全局变量
template<typename T> 
void fun(int b, T c, T d) //定义函数模板
{
  ......
  a = b;
}

  头文件:

//------fun.h或fun.hpp------//   //错误做法
#ifndef _FUN_H_
#define _FUN_H_

extern a;
template<typename T> void fun(int b, T c, T d); 

#endif

  经过尝试,按照普通函数的方式将函数模板的定义和声明分开,在其它文件中调用函数模板,编译时就会出现“找不到该函数定义”的错误。

  那么有没有办法将函数模板的定义和声明正确分开,提供给其它文件包含调用呢,答案肯定是有的。

3、问题的解决

  针对上述第2点所阐述的函数模板使用的这一场景,需要将函数模板的定义和声明分离开来,根据实际的应用,使用以下的做法可以很好的解决这一问题,编译和调用都没有问题。

  首先是源文件*.cpp的实现:

//------fun.cpp------//
using namespace std;
int a; //定义全局变量
template<typename T> 
void fun(int b, T c, T d) //定义函数模板
{
  ......
  a = b;
}

template void fun(int b, int c, int d);  //函数模板实例化,此时T被int替代 
template void fun(int b, char c, char d); //函数模板实例化,此时T被char替代

  因此在源文件中操作有:

  (1)、定义需要使用的函数模板;

  (2)、在定义的函数模板后进行函数实例化操作,通过这样的方法实现具体的模板函数。
  
  接着是头文件.h或者.hpp的实现:

//------fun.h或fun.hpp------//
#ifndef _FUN_H_
#define _FUN_H_

extern a;
template<typename T> void fun(int b, T c, T d); 
extern template void fun(int b, int c, int d);
extern template void fun(int b, char c, char d); 

#endif

  因此在头文件中需要的操作有:

  (1)、声明定义的函数模板;

  (2)、使用extern的方式声明实例化后的模板函数。

总结

  可见,将函数模板的定义和声明分开,需要额外在源文件中进行函数模板的实例化再在头文件中进行声明,多了一些步骤。在无特定的使用的场景中,还是建议将函数模板放在头文件中直接定义并调用;当然,如果碰到一些跨文件调用的特定场景,那么采用这种将函数模板的定义和声明分开的方法也是OK的。


更多技术内容和书籍资料获取敬请关注公众号“明解嵌入式”

1 声望
0 粉丝
0 条评论
推荐阅读
重载的奥义之函数重载
&emsp;&emsp;重载,顾名思义从字面上理解就是重复装载,打一个不恰当的比方,你可以用一个篮子装蔬菜,也可以装水果或者其它,使用的是同一个篮子,但是可以用篮子重复装载的东西不一样。

Sharemaker阅读 551

封面图
最好用的 python 库合集
🎈 分词 - jieba优秀的中文分词库,依靠中文词库,利用词库确定汉子之间关联的概率,形成分词结果 {代码...} 🎈 词云库 - wordcloud对数据中出现频率较高的 关键词 生成的一幅图像,予以视觉上的突出 {代码...} 🎈 ...

tiny极客11阅读 3.4k评论 2

封面图
疫情已过,2023 我的前端面试记录
顺利入职。把我最近找工作的心得记录下来。工作交接确定 lastday整理手头工作,相关对接人、交接人放文档中工作交接过渡阶段。做好被咨询者,该拉人拉人,该拉群拉群平时沟通顺畅的同事如果没有 WX 可以加一个属...

linong11阅读 1.1k

数据结构与算法:二分查找
一、常见数据结构简单数据结构(必须理解和掌握)有序数据结构:栈、队列、链表。有序数据结构省空间(储存空间小)无序数据结构:集合、字典、散列表,无序数据结构省时间(读取时间快)复杂数据结构树、 堆图二...

白鲸鱼9阅读 6.5k

花了几个月时间把 MySQL 重新巩固了一遍,梳理了一篇几万字 “超硬核” 的保姆式学习教程!(持续更新中~)
MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一。

民工哥13阅读 1.9k

封面图
思否 CTO 祁宁:社区问答是激荡高级智慧的头脑风暴
在祁宁家里,有一套完整的赛车模拟器,他甚至还请人到国外代购了最新的 VR 设备。作为沉浸式赛车游戏发烧友,除了享受速度与激情带来的愉悦感,祁宁在玩的过程中更多的是思考如何将技术能力进行产品化的问题。

万事ONES6阅读 12.9k评论 1

封面图
算法可视化:一文弄懂 10 大排序算法
在本文中,我们将通过动图可视化加文字的形式,循序渐进全面介绍不同类型的算法及其用途(包括原理、优缺点及使用场景)并提供 Python 和 JavaScript 两种语言的示例代码。除此之外,每个算法都会附有一些技术说...

破晓L7阅读 827

封面图
1 声望
0 粉丝
宣传栏