为什么 c++中函数模板和类模板的 声明与定义需要放到一起?

将模板的声明与定义写在一起实在很不优雅。尝试用“传统”方法,及在.h文件里声明,在.cpp文件里定义,

然后在main函数里包含.h头文件,这样会报链接错误。why!!!!!!!!!!!!!

这是因为函数模板要被实例化后才能成为真正的函数,在使用函数模板的源文件中包含函数模板的头文件,(carefully!!!)

如果该头文件中只有声明,没有定义,那编译器无法实例化该模板,最终导致链接错误。(类模板同样!!)

 

复制代码
 1 //---------------test.h-------------------// 
 2  void f();//这里声明一个函数f 
 3 //---------------test.cpp--------------// 
 4  #include”test.h” 
 5  void f() 
 6  { 
 7//do something 
 8  } //这里实现出test.h中声明的f函数 
 9 //---------------main.cpp--------------// 
10  #include”test.h” 
11  int main() 
12  { 
13      f(); //调用f
14  }
复制代码

编译时会生成两个obj文件,main.obj和test.obj,而在main.obj里并没有f函数的二进制代码,这些代码实际存在于test.obj中。

在main.obj中对 f 的调用只会生成一行call指令,call指令的地址由链接器生成

 

复制代码
 1 //-------------test.h----------------// 
 2  template<class T> 
 3  class A 
 4  { 
 5     public: 
 6      void f(); //这里只是个声明 
 7  }; 
 8 //---------------test.cpp-------------// 
 9  #include”test.h” 
10  template<class T> 
11  void A<T>::f() 
12  { 
13//do something 
14  } 
15 //---------------main.cpp---------------// 
16  #include”test.h” 
17  int main() 
18  { 
19      A<int> a; 
20     a. f(); 
21  }
复制代码

我们知道模板有个具现化的过程,在未被使用的时候是不会生成二进制文件的。所以当链接器去找f函数的地址时,因为在这之前没有调用过f(),test.obj里自然就没有f函数的二进制代码,于是就会报错。

 

要使模板声明与定义分开也不是没有办法。

第一种办法是在main函数里包含cpp文件

复制代码
 1 //-------------test.h----------------// 
 2  template<class T> 
 3  class A 
 4  { 
 5     public: 
 6      void f(); //这里只是个声明 
 7  }; 
 8 //---------------test.cpp-------------// 
 9  #include”test.h” 
10  template<class T> 
11  void A<T>::f() 
12  { 
13//do something 
14  } 
15 //---------------main.cpp---------------// 
16  #include”test.cpp” //careful!!!!!!!!!
17  int main() 
18  { 
19      A<int> a; 
20     a. f(); 
21  }
复制代码

这样三个文件的内容通过include实际上包含在同一个文件里,自然就不会出错了

 

复制代码
 1 //-------------test.h----------------// 
 2  template<class T> 
 3  class A 
 4  { 
 5     public: 
 6      void f(); //这里只是个声明 
 7  }; 
 8 #include<test_impl.h>
9 //---------------test_impl.h-------------// 10 template<class T> 11 void A<T>::f() 12 { 13//do something 14 }
15 //---------------main.cpp---------------// 16 #include”test.h” 17 int main() 18 { 19 A<int> a; 20 a. f(); 21 }
复制代码

这两种方法实际上都是包含编译,没有本质的区别,不过感觉第二种方法看起来比较舒服

 

复制代码
 1 //-------------test.h----------------// 
 2  template<class T> 
 3  class A 
 4  { 
 5     public: 
 6      void f(); //这里只是个声明 
 7  }; 
 8 //---------------test.cpp-------------// 
 9  #include”test.h” 
10  template<class T> 
11  void A<T>::f() 
12  { 
13//do something 
14  } 
15 template class A<int>;//!!!!!!在这里实现了具现了类型  这样编译就不会有问题了  但是这样不太好  自己想为什么 !!!
16 //---------------main.cpp---------------// 
17  #include”test.h” 
18  int main() 
19  { 
20      A<int> a; 
21     a. f(); 
22  }
复制代码

 

差不多了吧 就这样OK了  其实是看的别人的博客!

 

posted on   zhangkele  阅读(3546)  评论(0编辑  收藏  举报

编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示