您的示例是正确的,但不是很便携。还有一种更简洁的语法可以使用(正如@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