结论
- 不建议将模板类的声明和定义分开,一般把这个文件叫做
.hpp
,以便与.cpp
文件进行区分,表示这个文件中使用了模板 - 模板类进行了两次编译,在第二次编译(模板类使用的时候)时才会生成真正的调用函数
- 声明和定义分开方式一:可将定义放在同名的
.inl
或.tpp
文件中,同时在头文件的末尾使用#include
包含进来,不可放到.cpp
文件,否则会造成嵌套包含 - 声明和定义分开方式二:在头文件中声明实例化,并在一个源文件中定义它,这样它只会被实例化一次,而不是每个翻译单元,这可能会加快编译速度,但是只能使用已经显式声明的模板类实例
- 不建议在模板类中使用友元函数
声明和定义在同一文件
// Test.h
// 写法二:前置声明
template<typename T> class Test;
template<typename T> void printTest(Test<T>& t);
template<class T>
class Test
{
public:
Test(T t){ m_t = t;};
// explicit Test(T t){ m_t = t;};
// 1. 直接在类内定义
// T getValue(){
// return m_t;
// };
// 2. 类外定义
T getValue();
// 3. 不建议在模板类中使用友元
// 写法一:不建议这种写法,与编译器有关
// VS 编译通过
// g++ 编译失败
// template<typename T>
// friend void printTest(Test<T>& t);
// 写法二:
// 首先需要前置声明
friend void printTest<T>(Test<T>& t);
private:
T m_t;
};
// 类外定义
template<typename T>
T Test<T>::getValue(){
return m_t;
};
template<typename T>
void printTest(Test<T>& t){
cout << typeid(t.m_t).name() << endl;
cout << t.m_t << endl;
}
// main.cpp
int main()
{
Test<int> tInt(1);
cout << tInt.getValue() << endl;
Test<double> tD(1.15);
cout << tD.getValue() << endl;
Test<float> tF = 1.3f; // 构造函数没有explicit修饰,可以隐式转换
cout << tF.getValue() << endl;
cout << "friend function------------\n";
printTest(tF);
return 0;
}
输出:
Start
1
1.15
1.3
friend function------------
f
1.3
Finish
声明和定义分开
方式一
定义放在同名的 .inl
或 .tpp
文件中,同时在头文件的末尾使用 #include
包含进来,不可放到 .cpp
文件,否则会造成嵌套包含
// Test.h
#include <iostream>
using namespace std;
template<class T>
class Test
{
public:
Test(T t){ m_t = t;};
// explicit Test(T t){ m_t = t;};
T getValue();
private:
T m_t;
};
// 包含定义所在的文件
#include "Test.tpp"
// Test.tpp
template<typename T>
T Test<T>::getValue(){
cout << typeid(m_t).name() << endl;
return m_t;
};
// main.cpp
#include <iostream>
#include "Test.h"
using namespace std;
int main()
{
Test<int> tInt(1);
cout << tInt.getValue() << endl;
Test<double> tD(1.15);
cout << tD.getValue() << endl;
Test<float> tF = 1.3f; // 构造函数没有explicit修饰,可以隐式转换
cout << tF.getValue() << endl;
return 0;
}
Output:
i
1
d
1.15
f
1.3
方式二
在头文件中声明实例化,并在一个源文件中定义它,但是只能使用已经显式声明的模板类实例
// Test.h
template<class T>
class Test
{
public:
Test(T t){ m_t = t;};
// explicit Test(T t){ m_t = t;};
T getValue();
private:
T m_t;
};
// 显式声明实例化
extern template class Test<int>;
typedef Test<float> TestF;
using TestD = Test<double>;
// Test.cpp
#include <iostream>
#include "Test.h"
using namespace std;
template<typename T>
T Test<T>::getValue(){
cout << typeid(m_t).name() << endl;
return m_t;
};
// 显式实例化
template class Test<int>;
template class Test<float>;
template class Test<double>;
// main.cpp
#include <iostream>
#include "Test.h"
using namespace std;
int main()
{
Test<int> tInt(1);
cout << tInt.getValue() << endl;
Test<double> tD(1.15);
// TestD tD(1.15);
cout << tD.getValue() << endl;
// Test<float> tF = 1.3f;
TestF tF = 1.3f; // 构造函数没有explicit修饰,可以隐式转换
cout << tF.getValue() << endl;
return 0;
}
Output:
i
1
d
1.15
f
1.3
参考: