为什么模板类的实现放在cpp会出错

在编译用到了模板类的编译单元时,编译器需要访问方法或者类的实现,以实例化它们。 如果这些实现不在头文件中,则它们将不可访问,因此编译器将无法实例化模板,进而会导致编译出错。

模板类的定义必须放在.h文件中吗

答案是否定的,模板类的几种写法

声明和定义都放在.h中

// Foo.h
template <typename T>
struct Foo
{void doSomething(T param) {}
};

声明和定义分隔开

// Foo.h
template <typename T>
struct Foo
{void doSomething(T param);
};#include "Foo.tpp"// Foo.tpp
template <typename T>
void Foo<T>::doSomething(T param)
{//implementation
}

实现放在了Foo.tpp,然而还是include Foo.tpp在了头文件中,只是做了文件的分隔,但是其他文件include了头文件后,还是把实现也包含进去了,跟都放在.h效果类似,只是实现和声明分开,代码可读性更好

声明放在.h文件,定义放在.cpp文件

// Foo.h
// no implementation
template <typename T> struct Foo { ... };//----------------------------------------    // Foo.cpp
// implementation of Foo's methods// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float

正在的声明和实现分离,include了头文件之后不会把实现的代码也加入到该编译单元。然后需要将所有用到的类型都显式的实例化

什么是实例化

编译器怎么处理模板呢?本质上来说,模板可被编译器产生多种多样函数和类的代码。只有当被使用的时候,编译器才会产生用于这种类型的代码。模板不是直接编译成以后需要的各种类的实现,而是将模板代码解析放入内存中,当编译某个编译单元需要用到该模板,则会按需产生相应的代码。实例化是编译器确定特定模板与特定参数集一起使用并在模板上执行参数替换以生成要编译的类或函数以及最终编译为模板的二进制代码的过程。

有两种类型模板实例化,隐式和显式。
显式的实例化见第三段代码,Foo.cpp中显式实例化了Foo和Foo。显式实例化会实例化所有的成员函数。
隐式实例化是一种按需实例化。当你使用一个模板类时,编译器才会进行实例化。例如如果你使用vector,此时编译器才会创建一个vector类型,并且只会实例化所需要的函数。不实例化所有的成员函数主要有两点原因:
1、节省编译时间
2、不同的成员函数对类型属性有一些要求,不实例化可以使得更多的类型可以用到模板类。例如map的operator[]操作符要求value是有默认构造函数的,因为你通过不存在的key访问value的时候会新建一个value,且是需要调用默认构造函数。如果你不需要这个操作,用find和insert也可以达到目标,这样对value的类型就少了一个限制,使得模板类更具有普适性。

特化template时遇到的duplicate symbol问题

stackoverflow上面有案例,详见multiple definition of template specialization when using different objects

Intuitively, when you fully specialize something, it doesn’t depend on a template parameter any more – so unless you make the specialization inline, you need to put it in a .cpp file instead of a .h or you end up violating the one definition rule as David says. Note that when you partially specialize templates, the partial specializations do still depend on one or more template parameters, so they still go in a .h file.

翻译过来:
直观地说,当你完全特化某些东西时,它不再依赖于模板参数,所以除非你使内联专业化,你需要将它放在.cpp文件中而不是.h中,否则你最终会违反大卫说的一个定义规则。 请注意,当您对模板进行部分特化时,部分特化仍然依赖于一个或多个模板参数,因此它们仍然位于.h文件中。
推荐的写法为:
举例,template中static成员的初始化,完全特化实现在.cpp中

template<> int B<A, 1>::a[1] = { };

声明在.h中

template<> int B<A, 1>::a[1];

详见static member initialization for specialized template class

参考文档

Why can templates only be implemented in the header file?
Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?
c++ 模板类 声明和定义都放在.h文件的原因
Meaning of ‘instantiation’ with respect to templates

C++模板类声明和定义几种写法相关推荐

  1. c++模板类声明和定义的问题

    这里在学习的过程中遇到的一些问题,比较简单,但还是记下来,以免下次遇到这个问题再犯,大佬们可跳过哦.先简单的介绍下模板的概念 C++模板(泛型编程) c++模板是泛型编程的基础,所谓泛型编程也是一种思 ...

  2. VS2017 函数模板和类模板的声明、定义和使用

    模板的声明.定义分为两种. 1 将模板的声明和定义都放在头文件中,在主程序的文件中包含此头文件即可 2 将模板的声明和定义分开编写. 在<C++ primer>教材中,将模板的声明和定义分 ...

  3. 关于C++模板函数声明与定义的问题

    关于C++模板函数声明与定义的问题 关于C++模板函数声明与定义的问题 模板函数出现的问题 模板函数问题解决 模板函数出现的问题 今天在写代码的时候,发现了一个关于模板函数的问题.如下所示, demo ...

  4. 在类模板的声明和定义中把.h与.cpp分离

    看了几位大吓的回复,深深地感觉到了这篇附笔中可能存在错误,于是把最初遇到此问题时的环境再模拟了一下,现在可以确认这篇附笔中的确存在问题,现在就修正一下,并对各位表示歉意. 6月初的一个项目中需要写一个 ...

  5. C++ 中的模板类声明头文件和实现文件分离后,如何能实现正常编译?

    著作权归作者所有. 商业转载请联系作者获得授权,非商业转载请注明出处. 作者:余天升 链接:http://www.zhihu.com/question/20630104/answer/15722407 ...

  6. 定义一个类mymath_C++:模板类

    22.模板类 22.1 模板类 模板是泛型编程的基础,那什么是泛型编程呢?泛型编程是一种独立于任何特定数据类型编写代码的方式. C++标准模板库中的数据容器.迭代器和算法,都是泛型编程的例子,它们都使 ...

  7. c++模板声明与定义

    c++ 模板声明与定义 文章目录 c++ 模板声明与定义 前言 模板实例化 为什么C++编译器不能支持对模板的分离式编译 重点 C++模板代码的组织方式 --包含模式(Inclusion Model) ...

  8. 【C++】模板函数的声明和定义必须在同一个文件中

    1.问题描述 习惯性的将函数的定义和实现,分别写在头文件和源文件(.cpp)中.今天也按照这个习惯实现了一个模板函数.然后编译时报错 ... error: undefined reference to ...

  9. 为什么C++模板声明与定义要放在同一文件中?

    写了3年多C++程序,很少用到模板,我靠,今天想试一下,照着别人的例子写,什么鬼,怎么都运行不过,仔细比对代码才发现,C++模板的声明与定义必须放到一个文件,.h或者.hpp, 真是颠覆我对C++编程 ...

最新文章

  1. javascript函数嵌套时arguments的问题
  2. Typora markdown公式换行等号对齐_【精品软件 第3期】 有颜有料的编辑器——Typora
  3. JQuery插件:ScrollTo平滑滚动到页面指定位置
  4. AVFoundation和 GPUImage初探
  5. docker install
  6. Bigo 实时计算平台建设实践
  7. 掌握Rabbitmq几个重要概念,从一条消息说起
  8. 浅析 Linux 初始化系统(系统服务管理和控制程序/Init System) -- sysvinit/systemvinit(System V init)
  9. 前端开发 简单表格的编写练习 0228
  10. mysql使用innodb需要注意的情况
  11. 窗体传值,子窗体,父窗体,反射,reflection,windows,组策略,gpedit.msc,动态创建窗体,谢谢...
  12. 【UVA11059】Maximum Product(set+set默认从大到小排列---水题)
  13. linux安装红警教程,红警2任务安装教程_红色警戒2任务安装方法一览
  14. 搜狐新闻数据400w+
  15. OS学习笔记-20(清华大学慕课)死锁和进程通信
  16. 学习Java第十九天(一):1、Java中的网络编程
  17. Dharma暴跌过度解读了吗?去中心化不足,模式难持续是关键
  18. @Scheduled使用及讲解
  19. 如何安装seed_ubantu20.04
  20. 复古纹路绩效考核培训PPT模板

热门文章

  1. 大学生Dreamweaver网页设计基础知识
  2. 疫情加速IT人才外包服务普及应用~
  3. 防火墙导致web访问不到服务器解决方法
  4. csv库的作用、怎么用、以及csv库的操作快速上手
  5. 软件设计模式之单例模式-----身份证号码---打印池
  6. 基于Attention的自动标题生成
  7. 数据分析师+前途无忧爬虫分析
  8. redhat双系统Linux引导,用wingrub引导windows与redhat linux双系统
  9. 蓝色数据分析关系折线图表格图表合集PPT模板
  10. c#中的interface abstract与virtual的介绍与使用