我有一些模板代码,我宁愿存储在一个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

其他回答

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

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

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

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

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

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

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

你举的例子没有问题。但是我必须说,我认为在cpp文件中存储函数定义是不有效的。我只理解需要将函数的声明和定义分开。

当与显式类实例化一起使用时,Boost Concept Check Library (BCCL)可以帮助您在cpp文件中生成模板函数代码。

以上都不适合我,所以这里是如何解决它,我的类只有1个方法模板..

.h

class Model
{
    template <class T>
    void build(T* b, uint32_t number);
};

.cpp

#include "Model.h"
template <class T>
void Model::build(T* b, uint32_t number)
{
    //implementation
}

void TemporaryFunction()
{
    Model m;
    m.build<B1>(new B1(),1);
    m.build<B2>(new B2(), 1);
    m.build<B3>(new B3(), 1);
}

这样可以避免链接错误,并且根本不需要调用TemporaryFunction