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

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)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • 查看linux执行的命令记录_shell 调用history

    查看linux执行的命令记录_shell 调用history前言我们每次敲打linux命令的时候,有时候想用之前用过的命令,一般情况下,我们都会按↑↓箭头来寻找历史的命令记录,那如果我想用1天前执行的某条命令,难道还要按↑100次?显示这样是不现实的,我们可

    2022年7月30日
  • linux iptable命令用法

    linux iptable命令用法iptables命令是Linux上常用的防火墙软件,是netfilter项目的一部分。可以直接配置,也可以通过许多前端和图形界面配置。语法iptables(选项)(参数)选项-t&lt;表&gt;:指定要操纵的表;-A:向规则链中添加条目;-D:从规则链中删除条目;-i:向规则链中插入条目;-R:替换规则链中的条目;-L:显示规则链中已有的条目;-F:清楚规则链中已…

    2022年5月28日
  • mybatis拦截器修改sql_javaweb拦截器是什么

    mybatis拦截器修改sql_javaweb拦截器是什么/***Copyright2009-2016theoriginalauthororauthors.**LicensedundertheApacheLicense,Version2.0(the”License”);*youmaynotusethisfileexceptincompliancewiththeLicense.*…

    2022年9月8日
  • IDEA搭建SpringBoot框架[通俗易懂]

    IDEA搭建SpringBoot框架[通俗易懂]IDEA搭建SpringBoot框架

    2022年8月20日
  • vue webpak版本 查看_vue升级webpack版本到webpack4

    vue webpak版本 查看_vue升级webpack版本到webpack4用vue来实验下,目的是把webpack3升级至^4.0更新思路是先更新看是否废弃掉了yarnglobaladd@vue/cli-initvueinitwebpackhello_vuecdhello_vuenpmstart需要先验证工程是正确的,可以运行后去看下webpack版本发现是3.6npminstallwebpack@latest-Dnpminstallw…

    2022年8月10日
  • C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」

    C/C++指针详解之基础篇(史上最全最易懂指针学习指南!!!!)「建议收藏」目录一.变量的内存实质到1.1变量的实质1.2赋值给变量1.3变量在哪里?二.指针是个什么东西?三.二级指针(指针的指针)3.1定义与初始化3.2间接数据访问3.2.1.改变一级指针指向3.2.2改变N-1级指针的指向3.2.3二级指针的步长四.指针与数组4.1指针与数组名4.1.1通过数组名访问数组元素4….

    2022年5月22日

发表回复

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

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