你的例子是正确的,但不是很容易移植。
还可以使用一种稍微干净一点的语法(如@namespace-sid等指出的那样)。
然而,假设模板化类是某个库的一部分,该库将被共享…
是否应该编译模板化类的其他版本?
库维护者是否应该预测类的所有可能的模板使用?
另一种方法
在源代码中添加第三个文件,即模板实现/实例化文件。
Lib /foo.hpp -从库
#pragma once
template <typename T>
class foo {
public:
void bar(const T&);
};
Lib /foo.cpp -直接编译这个文件只会浪费编译时间
// Include guard here, just in case
#pragma once
#include "foo.hpp"
template <typename T>
void foo::bar(const T& arg) {
// Do something with `arg`
}
foo.MyType.cpp -使用库,显式模板实例化foo<MyType>
// Consider adding "anti-guard" to make sure it's not included in other translation units
#if __INCLUDE_LEVEL__
#error "Don't include this file"
#endif
// Yes, we include the .cpp file
#include <lib/foo.cpp>
#include "MyType.hpp"
template class foo<MyType>;
按照需要组织你的实现:
所有实现都在一个文件中
多个实现文件,每种类型一个
每个类型集的实现文件
为什么? ?
这种设置应该减少编译时间,特别是对于大量使用的复杂模板代码,因为您不必在每个模板中重新编译相同的头文件
翻译单元。
它还能够更好地检测哪些代码需要由编译器和构建脚本重新编译,从而减少增量构建负担。
用法示例
foo.MyType.hpp -需要知道foo<MyType>的公共接口,而不是.cpp源
#pragma once
#include <lib/foo.hpp>
#include "MyType.hpp"
// Declare `temp`. Doesn't need to include `foo.cpp`
extern foo<MyType> temp;
cpp -可以引用本地声明,但也不重新编译foo<MyType>
#include "foo.MyType.hpp"
MyType instance;
// Define `temp`. Doesn't need to include `foo.cpp`
foo<MyType> temp;
void example_1() {
// Use `temp`
temp.bar(instance);
}
void example_2() {
// Function local instance
foo<MyType> temp2;
// Use templated library function
temp2.bar(instance);
}
cpp -可以与纯头模板一起工作的例子,但在这里不能
#include <lib/foo.hpp>
// Causes compilation errors at link time since we never had the explicit instantiation:
// template class foo<int>;
// GCC linker gives an error: "undefined reference to `foo<int>::bar()'"
foo<int> nonExplicitlyInstantiatedTemplate;
void linkerError() {
nonExplicitlyInstantiatedTemplate.bar();
}
注意:大多数编译器/linter /代码助手不会将此检测为错误,因为根据c++标准没有错误。
但是当你把这个翻译单元链接到一个完整的可执行文件时,链接器不会找到foo<int>的定义版本。
替代方法:https://stackoverflow.com/a/495056/4612476