关于模板函数声明与定义的问题[通俗易懂]

c++primer上说:c++模板函数的声明与定义通常放在头文件中,而普通的函数通常是声明放在头文件中,定义放在源文件中,为什么会有这样的区别呢?模板函数与普通成员函数到底有什么区别?测试代码:tem.h#ifndef_TEM_H#define_TEM_HtemplateTadd(Ta,Tb);//{//returna+b;//}

大家好,又见面了,我是你们的朋友全栈君。

c++ primer上说:c++模板函数的声明与定义通常放在头文件中,而普通的函数通常是声明放在头文件中,定义放在源文件中,为什么会有这样的区别呢?模板函数与普通成员函数到底有什么区别?


测试代码:

tem.h

#ifndef _TEM_H
#define _TEM_H
template<typename T> T add(T a,T b);
//{

//return a+b;
//}
#endif

tem.cpp

using namespace std;
//#include “tem.h”

template <typename T> T add(T a, T b){return a+b;}
template int add(int,int);//实例化定义,必须放在模板定义的后面

main.cpp

#include <iostream>
#include “tem.h”
using namespace std;
int main()
{

cout << add(1,2);
return 0;
}

首先明确:

对普通函数来说,声明放在头文件中,定义放在源文件中,其它的地方要使用该函数时,仅需要包含头文件即可,因为编译器编译时是以一个源文件作为单元编译的,当它遇到不在本文件中定义的函数时,若能够找到其声明,则会将此符号放在本编译单元的外部符号表中,链接的时候自然就可以找到该符号的定义了。

而对模板函数来说,首先明确,模板函数是在编译器遇到使用模板的代码时才将模板函数实例化的。若将模板函数声明放在tem.h,模板定义放在tem.cpp,在main.cpp中包含头文件,调用add,按道理说应该实例化int add(int,int)函数,即生成add函数的相应代码,但是此时仅有声明,找不到定义,因此此时,它只会实例化函数的符号,并不会实例化函数的实现,即这个时候,在main.o编译单元内,它只是将add函数作为一个外部符号,这就是与普通函数的区别,对普通函数来说,此时的add函数已经由编译器生成相应的代码了,而对模板函数来说,此时并没有生成add函数对应的代码。此时编译main.cpp单元不会报错,但链接就会出现add函数未定义的错误。

因此,我们可以通过显式的实例化定义,即通过加上语句temmplate int add(int,int),编译器看到此语句将会生成add方法的int版本,这样的话,再链接就不会报错了。此外,这样做通常也能够提高编译的效率。试想,如果在tem.h文件内定义模板,假如有三个源文件均包含了该头文件且均使用了模板(假定均调用了add模板的int版本),则在这三个源文件内必然都会生成add函数的实例。显然效率不高。而如果像上面那样使用该模板,则只会在tem.cpp文件中实例化。

最后,对于类模板来说,也同样符合上面的原则。在实际类模板的实例化时,实际上是分几步的,首先当然是类模板的实例化,然后还有类成员函数的实例化,我们知道在类的定义中,其实只是声明了类的成员函数,编译器实际上是把类的成员函数编译成修改名称后的全局函数的,因此在使用类模板的时候,首先会初始化类模板,同时初始化类模板相应的构造函数,使用类模板的实例调用相应的成员函数时,才会初始化类模板的成员函数。如果类模板的成员函数的定义与类的定义不在同一个编译单元中(分离式编译),此时调用类的成员函数便会出现未定义的错误。而当我们像代码中那样在某个地方显式的调用它的时就不会出现此类问题了。

总结:其实很明显,明确一点就可以了,即编译器只要遇到使用模板函数时就会实例化相应的函数,若在此编译单元内没有模板函数的定义,它当然不能够实例化成功了。因此通常情况下模板函数的声明与定义均放在同一文件内,因此这样就保证了在使用模板的地方一定可以实例化成功了。同时,由编译器保证只生成某种类型的一个实例版本,不用担心重复实例化的问题。

c++primer上面只说了类模板的成员函数可以不在头文件中定义,却始终感觉说得不清不楚,因为实际上像普通类那样类的定义与实现放在不同的文件中的话,是会链接出错的。总之,若你不想出现任何未定的错误,将类模板或函数模板的定义与声明放在同一个文件中就行了。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/126778.html原文链接:https://javaforall.net

(0)
上一篇 2022年4月17日 下午2:00
下一篇 2022年4月17日 下午2:20


相关推荐

  • 2021.4.14永久激活码_通用破解码

    2021.4.14永久激活码_通用破解码,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月16日
    1.4K
  • FAT文件系统详解

    FAT文件系统详解FAT 格式的文件系统 磁盘数据分为 5 部分 MBR 区 DBR 区 FAT 区 DIR 区和 DATA 区 MBR 区 MBR MainBootReco 主引导扇区 意即为主引导记录区 位于整个硬盘的 0 磁道 0 柱面 1 扇区 在总共 512 字节的主引导扇区中 MBR 占用了 446 个字节 偏移 0 偏移 1BDH 另外的 64 个字节 偏移 1BEH 偏移 1FDH 交给了 DPT DiskPartitio 硬盘分区表

    2026年3月19日
    2
  • 如何做出美观的ArcGIS地图

    如何做出美观的ArcGIS地图如何做出美观的 ArcGIS 地图我相信 用过 arcGIS 的人来说 做一张 ArcGIS 地图肯定不难 但是 难在如何做出一副好看严谨的 ArcGIS 地图 1 投影首先 对于不同的国家要选择适合的坐标系 投影参考 http www ginkgomaps com maps world html 然后要分清投影坐标系和地理坐标系的区别 地理坐标系是球面坐标 参考平面是椭球面 坐标单位是经纬度

    2026年3月17日
    3
  • RBF(径向基)神经网络

    RBF(径向基)神经网络

    2021年11月20日
    46
  • js闭包使用场景

    js闭包使用场景https www cnblogs com jianghao233 p 8992289 html

    2026年3月18日
    1
  • Java 三元运算符

    Java 三元运算符运算符是三元运算符 Java 三元运算符基本上是简单的 if 语句的简写形式 语法 具有以下一般形式 expression1 expression2 expression3e 可以是计算为 boolean 值的任何表达式 如果 expression1 是 true 那么将评估 expression2 否则 将评估 expression3 计算的表达式是 操作的结果 需要 expression2 和 expression3 来返回相同的类型 这可以是无

    2026年3月26日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号