当前位置:网站首页>关于模板函数声明与定义的问题[通俗易懂]
关于模板函数声明与定义的问题[通俗易懂]
2022-07-26 10:17:00 【全栈程序员站长】
大家好,又见面了,我是你们的朋友全栈君。
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.cn/126778.html原文链接:https://javaforall.cn
边栏推荐
- [award-winning question] ask Judea pearl, the Turing prize winner and the father of Bayesian networks
- Distributed network communication framework: how to publish local services into RPC services
- Cause: could't make a guess for solution
- The charm of SQL optimization! From 30248s to 0.001s
- Tableviewcell highly adaptive
- Keeping alive to realize MySQL automatic failover
- Use of pclint in vs2013
- INSTALL_FAILED_SHARED_USER_INCOMPATIBLE错误解决方式
- Mysql5.7.25 master-slave replication (one-way)
- Li Kou - binary tree pruning
猜你喜欢
Error in render: "typeerror: cannot read properties of undefined (reading 'length')" --- error when calling interface
Uniapp common error [wxml file compilation error]./pages/home/home Wxml and using MySQL front provided by phpstudy to establish an independent MySQL database and a detailed tutorial for independent da
Production of a-modal drag function in antui
equals与==的区别
Mysql5.7.25 master-slave replication (one-way)
[award-winning question] ask Judea pearl, the Turing prize winner and the father of Bayesian networks
Learning about opencv (3)
Learning about tensor (III)
How to write a million reading article
数据库的复习--3.SQL语言
随机推荐
Learning about opencv (3)
Yarn 'TSC' is not an internal or external command, nor is it a runnable program or batch file. The problem that the command cannot be found after installing the global package
【Halcon视觉】编程逻辑
PMM (percona monitoring and management) installation record
Learning about tensorflow (I)
【Halcon视觉】图像灰度变化
Phpexcel export Emoji symbol error
简单化构造函数的继承方法(一)- 组合继承
Flask框架初学-03-模板
数通基础-STP原理
Docker configuring MySQL Cluster
在.NET 6.0中配置WebHostBuilder
数通基础-TCPIP参考模型
汉诺塔II|汉诺塔4柱
WARNING: [pool www] server reached pm. max_ children setting (5), consider raising it
【socket】三次握手是在listen中完成,accept只从完成连接的队列中拿出一个连接
Uniapp error 7 < Map >: marker ID should be a number
点赞,《新程序员》电子书限时免费领啦!
数通基础-Telnet远程管理设备
数通基础-网络基础知识