我有一些模板代码,我宁愿存储在一个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中使用这段代码,但我想移植到其他环境中。


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

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


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

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

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

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


这应该在支持模板的任何地方都能正常工作。显式模板实例化是c++标准的一部分。


在最新的标准中,有一个关键字(export)可以帮助缓解这个问题,但在我所知道的任何编译器中都没有实现,除了Comeau。

请参阅FAQ-lite。


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

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


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

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


This is definitely not a nasty hack, but be aware of the fact that you will have to do it (the explicit template specialization) for every class/type you want to use with the given template. In case of MANY types requesting template instantiation there can be A LOT of lines in your .cpp file. To remedy this problem you can have a TemplateClassInst.cpp in every project you use so that you have greater control what types will be instantiated. Obviously this solution will not be perfect (aka silver bullet) as you might end up breaking the ODR :).


对于本页上的其他人来说,想知道显式模板专门化(至少在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>;

是时候更新了!创建内联(。Inl,或可能是任何其他)文件,并简单地复制其中所有的定义。确保将模板添加到每个函数之上(template <typename T,…>)。现在,与在内联文件中包含头文件相反,您可以做相反的事情。在类声明之后包含内联文件(# Include "file.inl")。

我真的不知道为什么没有人提到这一点。我看不出有什么直接的缺点。


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


这是定义模板函数的标准方法。我认为有三种定义模板的方法。或者可能是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文件的后果。


让我们举一个例子,假设出于某种原因你想要一个模板类:

//test_template.h:
#pragma once
#include <cstdio>

template <class T>
class DemoT
{
public:
    void test()
    {
        printf("ok\n");
    }
};

template <>
void DemoT<int>::test()
{
    printf("int test (int)\n");
}


template <>
void DemoT<bool>::test()
{
    printf("int test (bool)\n");
}

如果你用Visual Studio编译这段代码,它可以开箱即用。 GCC将产生链接器错误(如果从多个.cpp文件中使用相同的头文件):

error : multiple definition of `DemoT<int>::test()'; your.o: .../test_template.h:16: first defined here

可以将实现移动到.cpp文件,但随后需要像这样声明类-

//test_template.h:
#pragma once
#include <cstdio>

template <class T>
class DemoT
{
public:
    void test()
    {
        printf("ok\n");
    }
};

template <>
void DemoT<int>::test();

template <>
void DemoT<bool>::test();

// Instantiate parametrized template classes, implementation resides on .cpp side.
template class DemoT<bool>;
template class DemoT<int>;

然后。cpp看起来是这样的:

//test_template.cpp:
#include "test_template.h"

template <>
void DemoT<int>::test()
{
    printf("int test (int)\n");
}


template <>
void DemoT<bool>::test()
{
    printf("int test (bool)\n");
}

如果头文件中没有最后两行- gcc可以正常工作,但是Visual studio会产生一个错误:

 error LNK2019: unresolved external symbol "public: void __cdecl DemoT<int>::test(void)" (?test@?$DemoT@H@@QEAAXXZ) referenced in function

如果你想通过.dll导出来公开函数,模板类语法是可选的,但这只适用于Windows平台-所以test_template.h可以像这样:

//test_template.h:
#pragma once
#include <cstdio>

template <class T>
class DemoT
{
public:
    void test()
    {
        printf("ok\n");
    }
};

#ifdef _WIN32
    #define DLL_EXPORT __declspec(dllexport) 
#else
    #define DLL_EXPORT
#endif

template <>
void DLL_EXPORT DemoT<int>::test();

template <>
void DLL_EXPORT DemoT<bool>::test();

使用前面示例中的.cpp文件。

然而,这给链接器带来了更多的头痛,所以如果你不导出.dll函数,建议使用前面的例子。


以上都不适合我,所以这里是如何解决它,我的类只有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