有关C++模板(template)的编译错误“error LNK2019: 无法解析的外部符号”的分析

C++报错信息:LNK2001:无法解析的外部符号 原因分析及解决方法 C++报错信息:LNK2001:无法解析的外部符号 解决方法及原因分析,error LNK2019: 无法解析的外部符号 阅读详情

转自:http://blog.youkuaiyun.com/fengyhack/article/details/39296411


按照通常的习惯,我们这样设计一个类或者结构(体):

在头文件(*.h  *.hh  *.hpp  *.hxx)中声明成员(或属性)和方法(假设为MyClass.hpp),

在源文件(*.c  *.cc  *.cpp  *.cxx)中包含该头文件(#include "MyClass.hpp")并实现类

或者结构(体)的方法

然后在调用方(比如main.cpp)包含该头文件(#include "MyClass.hpp")

这是一个很好的习惯,至少我是这么认为的


不愉快的事情时有发生。

如果坚持这个套路,我们编写一个模板,比如模板函数、模板类,哐当,在一个实际

应用中,出现了如下链接(LINK)错误:


这是一个用于测试的简化示例,具体代码结构如下


在MyTemplate中定义了一个testFunc模板函数,然后在主函数中调用了一个特化实例


声明如下:

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. // MyTemplate.h  
  2.   
  3. #ifndef MY_TMP_H  
  4. #define MY_TMP_H  
  5.   
  6. template <typename T>  
  7. void testFunc(T& x);  
  8.   
  9. #endif  

实现代码

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. // MyTemplate.cpp  
  2.   
  3. #include "MyTemplate.h"  
  4.   
  5. #include <iostream>  
  6. #include <typeinfo>  
  7. using namespace std;  
  8.   
  9. template <typename T>  
  10. void testFunc(T& x)  
  11. {  
  12.     cout <<"Type: "<< typeid(x).name() << endl;  
  13. }  


主函数引用

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. // Main.cpp  
  2.   
  3. #include "MyTemplate.h"  
  4.   
  5. #include <iostream>  
  6.   
  7. int main(void)  
  8. {  
  9.     double d = 0.0;  
  10.     testFunc(d);  
  11.     system("pause");  
  12.     return 0;  
  13. }  


分析以上代码,你可能会说:“没问题呀,怎么会出现链接错误呢?”

问题的根源在于编译器对于模板(template)的编译处理过程中,

大致是这样的(果真如此么?):

1、模板函数testFunc在编译(compile)期间并未生成具体二进制代码,

    在main函数中也没有嵌入这个函数的代码,可能只是包含了一句

    call testFunc之类的(稍后详述)


2、编译阶段,在main函数中发现了testFunc的引用,但是main.obj中没有相关的

     可执行代码(编译器认为该函数在别处定义,这就是为什么需要链接也就是

     LINK了,在main中虽然引用到testFunc但是只提供了一个call虚拟地址而没有

     实际的执行代码)


3、链接阶段,将各个模块(编译期间生成的很多*.obj文件)组织起来

     形象的说就是,在LINK的时候把testFunc“嵌入”进来,就像是一个子过程,

     在main中从调用处jump到这里即可,执行完毕再跳出子模块,从“中断点”

     继续执行后续语句)


4、模板在编译期间是不生成具体代码的,除非有特化的引用,比如上述的      

     testFunc(double d),这里将参数实例化为double



详细分析

这个示例中MyTemplate.h中声明了模板函数但是具体实现放在了MyTemplate.cpp

文件中,然后主函数中引用到testFunc的一个特化实例,因为MyTemplate和Main

分别编译为MyTemplate.obj和Main.obj

(根据你编译器的设置可能会有不同,这是按照默认设置生成的中间文件名称)


在编译MyTemplate的过程中,没有找到任何特化实例(头文件为模板声明,

源文件亦为模板实现),因此不生成任何可执行实例代码

主模块Main中引用到testFunc,并且main.cpp中没有相关实现代码

(#include "MyTemplate.h"只是包含了声明)

因此只是给出了call testFunc的“字样”而不是具体执行代码,就是说寄希望于

链接阶段,在别的模块中找到testFunc的定义


于是在LINK阶段需要查找testFunc的实现定义,不幸的是,找不到了,于是出现

链接错误

error LNK2019: 无法解析的外部符号 "void __cdecl testFunc<double>(double &)"

 (??$testFunc@N@@YAXAAN@Z),该符号在函数 _main 中被引用


那么,如何解决这个问题呢?


至少有以下几种方法:

1、在一个文件中完成模板的声明及实现

2、在模板头文件末尾添加实现文件的包含 #include "MyTemnplate.cpp"

3、在调用方(main.cpp中)包含实现文件 #include "MyTemnplate.cpp"


第二种方式还不如第一种方式简洁,实际上就是一个东西,

第三种方法可能会造成而外开销(比如多个模块都调用了这个模板的某个特化实例的

情形)

但一般来说这种开销不算什么,除非你的要求很严格,那么请采用第一种方式吧


采用第三种方法进行测试


设置一个断点,如下:



启动调试然后打开反汇编窗口(VS2013下的默认快捷键是 Ctrl+Alt+D)

注意红色方框标注的那一行


切换到call地址0B31299h所在行

找到了testFunc的特化实例 testFunc<double>


发现jmp到0B33610h


这个地址就是testFunc特化实例 testFunction<double>()的入口


如此便验证了本文开头的解释

事实上,除了模板,抽象类等也不能被实例化,在这种情况下,建议采用上述的

第一种方法:

在一个文件中完成模板的声明及实现

c++ 模板 声明与实现分开编写(error LNK2001: unresolved external symbol...) 对于所有的喜欢把声明和定义分开放的C++er,在写模板时要把它俩放在一起一定感到十分郁闷,于是我本着非要棒打鸳鸯的倾向,找到了几种“棒”~   欢迎各位完善棒打鸳鸯技巧~ 1.在模板的声明文件(.h)的最后一行 #include ".cpp" //模板的实现文件 2.在调用模板的文件在文件开头 #include ".cpp" //模板的实现文件 阅读详情

相关推荐

C++链接报错: error LNK2001: 无法解析的外部符号

一般说

m0_60352504的博客 1万+

C++无法解析的外部符号的3种可能

今天碰到C++程序在链接的过程中出现的”……无法解析的外部符号……”提示,google和baidu了半天发现很多人解决的办法都说的太过繁琐或者是针对某个具体的提示进行处理,其实很多时候对于初学者(吾等也是啊ORZ)无外乎碰到的只是很基本的三个地方需要去检查即可排除问题,这里分享出来,我碰到的问题是用第一个办法办法解决的: 1、lib 文件未引入。可使用“#pragma comment(lib,

一路走来,未留足迹 5304

error LNK2019: 无法解析的外部符号

VC 编码过程中: 1. 经常遇到的问题经常是,在头文件中定义了却未实现,然后在其他单元又调用产生的; 2. 值得注意的是,在使用template模板特化时,将类方法或者方法,定义实现在cpp文件中,在使用时,会产生此error,将实现部分移至.h文件即可。编译的文件,常常是产生obj临时文件,调用过程中,调用处会到obj中查找此实现,模板方法实现是在.h中的,放在cpp肯定是找不到的

走向CTO的路上... 711

C++显示无法解析的外部口令(符号-

C++显示无法解析的外部命令(符号)一、出现原因二、解决办法 一、出现原因 1.可能是类的构造函数没有函数体 二、解决办法 1.加上函数体,哪怕只写一个花括号。

撑一把纸伞,等一位佳人 4747

Python金融数据获取工具库之baostock使用详解

在金融数据分析和量化交易中,获取准确及时的市场数据是非常重要的。baostock是一个专门为中国股市数据提供支持的 Python 库,它提供了免费的股票数据接口,用户可以方便地获取股票、指数、基金等各种金融数据。本文将详细介绍baostock库,包括其安装方法、主要特性、基本和高级功能,以及实际应用场景,帮助全面了解并掌握该库的使用。

Rocky006的博客 5343

手把手推导APM的 sqrt_controller控制器

前言 玩过无人机的朋友对APM应该都不会陌生,如果是搞过多旋翼飞控且读过代码,肯定遇到过sqrt_controller控制器,它频繁的在位置、姿态控制中出现,所以非常有必要搞清楚这个控制原理。 下面以高度控制器为例来分析。 高度控制 APM的高度控制采用PID串级3环控制,即外环位置控制,中间速度控制,内环速度控制,如下图所示。 今天要讨论的sqrt_controller就是应用于位置控制器中。位置控制器的输出是速度控制器的参考reference输入,如果采用单P控制,其输出vel_ref = pos_e

周小逗的博客 3029

关于各种无法解析的外部符号问题的相应解决方案

<br />在使用vs2008调试程序的过程中,经常会出现无法解析的外部符号问题,可能的原因有很多种,下面这些是我一年来积累的经验.<br />仅供参考.<br /> <br />考虑可能的原因:<br />[0]出现无法解析可能是因为lib文件不正确,比如64位的编译配置,结果使用的是32位的lib包.<br />[1]只写了类声明,但还没有写实现类,造成调用时无法解析<br />[2]声明和定义没有统一,造成链接不一致,无法解析<br />[3]没有在项目属性页的链接器的命令行选项加入相应的类包。<br

enotswn的专栏 5万+

手把手教你用CODESYS将旧笔记本改造成软PLC(含证书配置避坑指南)

本文提供了一份详尽的指南,教你如何利用CODESYS平台将闲置旧笔记本改造为一台功能完整的软PLC。内容涵盖从硬件评估、系统实时性优化、软件安装与驱动配置,到关键的证书授权管理避坑指南,旨在帮助自动化工程师和爱好者低成本搭建稳定可靠的学习与开发平台。

weixin_29190573的博客 279

模板类 error LNK2019: 无法解析的外部符号

如果将类模板的声明和实现写在两个独立的文件中,在构建时会出现“error LNK2019: 无法解析的外部符号 ”的错误。 解决方法有: 第一种方法,就是把类模板中成员函数的声明和定义都放在类的定义中(.h文件),不要分开就行。 第二种方法,在主文件(main文件)中既包含类模板的声明文件(接口文件)(.h文件),同时也包含类模板的实现文件(.cpp文件)就行了。 ...

afl291529699的博客 161

SpeedyBee F405 V3 50A飞塔装机全攻略:从开箱到首飞避坑指南

本文提供SpeedyBee F405 V3 50A飞塔的详细装机指南,涵盖从开箱检查、硬件焊接、软件配置到安全首飞的全流程。重点剖析了电源连接、固件刷写等高频故障点,帮助FPV新手避开常见陷阱,确保一次成功组装并安全起飞。

weixin_29179311的博客 416

基于555定时器的呼吸灯电路

呼吸灯是指LED灯在芯片的控制下完成由亮到暗的逐渐变化,感觉就像人在呼吸,其广泛使用在手机上,起到一个通知提醒的作用。呼吸灯在许多灯光装饰上也有大量使用,如音响、汽车广告牌等,在生活中较为常见。...

qq1074237849的博客 9492

深入解析Windows C++动态库链接:从LNK2019错误到跨模块接口设计

在C++开发中,动态链接库(DLL)与静态链接库(LIB)是两种核心的代码复用与模块化技术。其本质区别在于链接阶段:静态库的代码被直接复制到最终可执行文件中,而动态库则在运行时通过导入表进行加载与地址绑定。这一机制依赖于符号可见性与名称修饰规则,在Windows平台上由`__declspec(dllexport/dllimport)`等编译器扩展实现,确保跨模块的函数与数据能正确寻址。理解这些原理不仅能解决常见的`LNK2019`链接错误,更能提升工程实践中的接口设计能力,例如通过智能宏实现静态库与动态库的

weixin_30897079的博客 380

优快云独家 | 全网首发 | 《目标检测蓝皮书》目录

10篇文章从目标检测小白变大神

Coding... 3万+

彻底解决Visual Studio C++链接错误LNK2001/LNK2019:原理、排查与实战

在C/C++程序开发中,编译与链接是将源代码转换为可执行文件的关键环节。编译阶段负责将单个源文件转换为包含机器代码和符号表的目标文件,而链接阶段则负责将所有目标文件和库文件组合起来,解析符号引用,生成最终的可执行程序。链接器在解析符号时,若找不到某个被引用符号的定义,就会抛出链接错误,这直接影响程序的构建与运行。理解链接错误的成因与排查方法,对于提升开发效率和代码质量至关重要。本文聚焦于Visual Studio环境下常见的LNK2001和LNK2019链接错误,通过分析符号解析、库依赖、编译设置一致性等核

an4455的博客 316

SIWAVE仿真PCB操作说明

使用SIWAVE进行PCB仿真提取S参数的操作

sinat_15150363的博客 3063

嵌入式u-boot最全面试题及参考答案(5万字长文持续更新)

例如,在一个便携式设备中,U-Boot 在启动时初始化电源控制器,将输出电压设置为合适的值。命令是u-boot构建过程中用于配置u-boot选项的命令。在嵌入式系统中,启动画面(也称为启动Logo或Splash Screen)是展示给用户的第一印象,它可以包含公司的标志、产品的名称或其他相关信息。u-boot允许通过环境变量来进行配置,这使得开发者可以根据不同的硬件平台和项目需求灵活地调整u-boot的行为。写入操作可以是字节级别的,也可以是块级别的,具体取决于设备的性能和操作系统的需求。

大模型大数据攻城狮的专栏 997

C++模板(template)使用注意事项(error LNK2019: 无法解析的外部符号)

通常写模板类都是在类定义里面来实现,一个头文件(.h)包含所有定义和声明代码。今天调试程序中无意识的发现程序有内存泄漏,原来是CListCtrl在使用过程中,节点关联了数据指针,以前都是在控件窗体销毁事件里面进行数据释放处理,但是发现每次释放都得在销毁事件里面进行处理非常麻烦,有没有简单点的处理方式呢?我就想窗体销毁的时候,控件也会要销毁的,于是想在控件销毁里面顺带吧节点数据也给销毁,这样是不是就省事很多呢?这样做确实省事方便了很多。由于我不想关心节点所关联的数据类型,于是获取节点数据,返回值类型统一采用L

老狼的专栏 1001

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

关于error LNK2001: unresolved external symbol ——分离编译

今天遇到一个很奇怪的链接错误:error LNK2001: unresolved external symbol(不确定的外部符号),我的是在调用call时,似乎找不到模板类中的函数。一般来说出现LNK2001,有两种情况,一是所引用的变量、函数不存在,拼写不正确或者使用错误;二是使用了不同版本的链接库。          综合以上,此处是在调用模板类的函数时发生了错误,很有可能是实例化的时候有

u013251005的专栏 753

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

C++模板编译与多文件链接冲突:从原理到解决方案

在C++编程中,编译与链接是构建可执行程序的两个核心阶段。编译器将源代码转换为目标文件,链接器则负责解析符号引用并将多个目标文件合并为最终程序。模板作为C++泛型编程的基础,其编译机制与传统的多文件分离编译模型存在根本性冲突。模板要求在编译期看到完整定义才能进行实例化,而传统模型允许声明与定义分离,这导致在链接阶段常出现“未解决的外部符号”错误。理解这一原理对于解决实际工程问题至关重要,特别是在涉及多文件组织和大型项目构建时。本文通过分析C++模板的实例化机制,探讨了定义置于头文件、显式实例化和extern

weixin_34218579的博客 432

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

上一篇: 红黑树浅析
下一篇: 在ubuntu中重置mysql服务器root密码的方法
big_bit
博客等级 码龄11年 67粉丝 40原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值