我有一些模板代码,我宁愿存储在一个CPP文件,而不是内联在头。我知道这是可以做到的,只要您知道将使用哪种模板类型。例如:

. h文件

class foo
{
public:
    template <typename T>
    void do(const T& t);
};

. cpp文件

template <typename T>
void foo::do(const T& t)
{
    // Do something with t
}

template void foo::do<int>(const int&);
template void foo::do<std::string>(const std::string&);

注意最后两行- foo::do模板函数只用于int和std::string,所以这些定义意味着应用程序将链接。

我的问题是-这是一个讨厌的黑客或这将与其他编译器/链接器工作吗?目前我只在VS2008中使用这段代码,但我想移植到其他环境中。


当前回答

你的例子是正确的,但不是很容易移植。 还可以使用一种稍微干净一点的语法(如@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

其他回答

对于本页上的其他人来说,想知道显式模板专门化(至少在VS2008中)的正确语法是什么(和我一样),它如下…

在你的.h文件中…

template<typename T>
class foo
{
public:
    void bar(const T &t);
};

在你的。cpp文件中

template <class T>
void foo<T>::bar(const T &t)
{ }

// Explicit template instantiation
template class foo<int>;

是的,这是做专门化显式实例化的标准方法。如您所述,您不能用其他类型实例化此模板。

编辑:根据评论进行更正。

这是定义模板函数的标准方法。我认为有三种定义模板的方法。或者可能是4个。每一种都有利弊。

Define in class definition. I don't like this at all because I think class definitions are strictly for reference and should be easy to read. However it is much less tricky to define templates in class than outside. And not all template declarations are on the same level of complexity. This method also makes the template a true template. Define the template in the same header, but outside of the class. This is my preferred way most of the times. It keeps your class definition tidy, the template remains a true template. It however requires full template naming which can be tricky. Also, your code is available to all. But if you need your code to be inline this is the only way. You can also accomplish this by creating a .INL file at the end of your class definitions. Include the header.h and implementation.CPP into your main.CPP. I think that's how its done. You won't have to prepare any pre instantiations, it will behave like a true template. The problem I have with it is that it is not natural. We don't normally include and expect to include source files. I guess since you included the source file, the template functions can be inlined. This last method, which was the posted way, is defining the templates in a source file, just like number 3; but instead of including the source file, we pre instantiate the templates to ones we will need. I have no problem with this method and it comes in handy sometimes. We have one big code, it cannot benefit from being inlined so just put it in a CPP file. And if we know common instantiations and we can predefine them. This saves us from writing basically the same thing 5, 10 times. This method has the benefit of keeping our code proprietary. But I don't recommend putting tiny, regularly used functions in CPP files. As this will reduce the performance of your library.

注意,我不知道臃肿的obj文件的后果。

您描述的问题可以通过在头文件中定义模板来解决,也可以通过上面描述的方法来解决。

我推荐阅读c++ FAQ Lite中的以下几点:

为什么我不能将模板类的定义与其声明分开,并将其放入.cpp文件中? 我如何避免链接错误与我的模板函数? c++关键字导出如何帮助处理模板链接器错误?

它们详细讨论了这些(和其他)模板问题。

此代码是格式良好的。您只需注意模板的定义在实例化时是可见的。引用标准§14.7.2.4:

非导出函数模板、非导出成员函数模板或类模板的非导出成员函数或静态数据成员的定义应出现在显式实例化的每个转换单元中。