查看原文
其他

模版定义一定要写在头文件中吗?

程序喵大人 程序喵大人 2022-08-22

大家在使用C++代码时或多或少都会使用到模板,使用模板时应该都是把定义放在了头文件中,因为放在源文件中定义会编译失败。


那问题来了,模板中的函数定义一定要写在头文件中吗?


先说结论:不一定要放在头文件中,定义也可以放在源文件中,但操作起来还是有点麻烦的。


继续往下看看


先看一段正常的模板代码:

// template.h#include <iostream>
template <typename T>struct TemplateTest { T value; void func();};
template <typename T>void TemplateTest<T>::func() { std::cout << typeid(T).name() << std::endl;}
// test_template.cc#include "template.h"
int main() { TemplateTest<int> test; test.func(); return 0;}

这段代码没啥毛病,因为实现放在了头文件中,也会正常输出。


如果我把函数定义放在源文件中呢,会怎么样?

// template.h#include <iostream>
template <typename T>struct TemplateTest { T value; void func();};
// template.cctemplate <typename T>void TemplateTest<T>::func() { std::cout << typeid(T).name() << std::endl;}
// test_template.cc#include "template.h"
int main() { TemplateTest<int> test; test.func(); return 0;}

嗯,不出意外,编译报错了,报了个没有某个函数实现的error:

/tmp/ccPghOjU.o: In function `main':test_template.cc:(.text+0x1f): undefined reference to `TemplateTest<int>::func()'collect2: error: ld returned 1 exit status

为什么没有此函数定义?


先补充个基础知识,模板的本质。本质其实就是类型泛化,可以用一个T代替多种类型,对于上面的模板,假如有此种使用:

TemplateTest<int> test;

那模板最终可能变成这样:

struct TemplateTest_int { int value; void func() { std::cout << typeid(int).name() << std::endl; }};

如果有这两种使用:

TemplateTest<int> test;TemplateTest<float> test;

那模板最终可能会变成这样:

struct TemplateTest_int { int value; void func() { std::cout << typeid(int).name() << std::endl; }};
struct TemplateTest_float { float value; void func() { std::cout << typeid(float).name() << std::endl; }};

模板最终会展开成什么样,取决于用户是怎么使用它的。


那回到上面的问题,为什么把定义放在源文件中,编译就失败了呢,因为每个源文件的编译都是独立的,尽管在test_template.cc进行了TemplateTesttest的使用,但是在template.cc中却感知不到,所以也就没法定义相关的实现。


思路来了,只需要让template.cc中感知到T有int类型的情况,那编译应该就没问题。这里有个语法,叫模板实例化,像这样:

template struct TemplateTest<int>;

把这行代码放在template.cc中:

// template.cc#include "template.h"
template <typename T>void TemplateTest<T>::func() { std::cout << typeid(T).name() << std::endl;}
template struct TemplateTest<int>;

整个代码的编译就没得问题了,通过这种方式即可以实现模板函数声明与实现的分离。


这也印证了上面的结论。


这里我再抛出几个问题,大家可以讨论讨论:

  • 模板的使用是否会导致代码段体积增大?怎么解决?

  • 模板的函数定义放在了头文件中,好多个源文件都include此头文件,是否会导致函数的multi definition类型的链接报错?实际使用中貌似没有报错,为什么?大家有想过吗?




往期推荐



为什么建议少用if语句!

工作8年,我决定离开上海

四万字长文,这是我见过最好的模板元编程文章!

如何正确的理解指针和结构体指针、指针函数、函数指针这些东西?

C++为什么要弄出虚表这个东西?

研究了一波RTTI,再介绍软件开发的201个原则,文末再送6本书

【性能优化】高效内存池的设计与实现

网传阿里裁员2万人…

腾讯 C++ 笔试/面试题及答案

介绍一个C++中非常有用的设计模式


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存