您的示例是正确的,但不是很便携。
还有一种更简洁的语法可以使用(正如@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;
examples.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);
}
error.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> 的定义版本。
如果没记错的话,我最初是从 SO 那里得到这个想法的。但是当我写下这个答案时,我一辈子都找不到原来的 SOA。今天,我想我找到了:https://stackoverflow.com/a/495056/4612476