大家在使用C++代码时或多或少都会使用到模板,使用模板时应该都是把定义放在了头文件中,因为放在源文件中定义会编译失败。
那问题来了,模板中的函数定义一定要写在头文件中吗?
先说结论:不一定要放在头文件中,定义也可以放在源文件中,但操作起来还是有点麻烦的。
继续往下看看
先看一段正常的模板代码:
// template.h
#include
template <typename T>
struct TemplateTest {
T value;
void func();
};
template <typename T>
void TemplateTest::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
template <typename T>
struct TemplateTest {
T value;
void func();
};
// template.cc
template <typename T>
void TemplateTest::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::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 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::func() {
std::cout << typeid(T).name() << std::endl;
}
template struct TemplateTest<int>;
整个代码的编译就没得问题了,通过这种方式即可以实现模板函数声明与实现的分离。
这也印证了上面的结论。
这里我再抛出 几个问题 ,大家可以讨论讨论:
全部0条评论
快来发表一下你的评论吧 !