我如何使用explicit模板实例化用于类定义中定义的模板成员函数

How can I use explicit template instantiation for template member functions defined within a class definition?

本文关键字:定义 函数 成员 实例化 何使用 explicit 用于      更新时间:2023-10-16

为了减少一个大型项目中的编译时间,该项目可以自由地使用模板,我使用" Extern模板"(显式模板实例化(获得了良好的结果,以防止通用模板功能从在许多不同的汇编单元中定义。

然而,关于它的一个令人讨厌的事情是,它不适用于类定义中定义的成员函数。

例如,我有以下模板类:

template <typename T>
struct Foo
{
    static T doubleIt(T input)
    {
        return input * 2;
    }
};

现在,我知道FOO最常用于数字类型,因此我将其添加到标题:

extern template struct Foo<int>;
extern template struct Foo<float>;
extern template struct Foo<double>;

和在CPP文件中,添加显式实例:

template struct Foo<int>;
template struct Foo<float>;
template struct Foo<double>;

这不起作用,如OBJ文件上的DumpBin.exe告诉我:

017 00000000 SECT4  notype ()    External     | ?doubleIt@?$Foo@M@@SAMM@Z (public: static float __cdecl Foo<float>::doubleIt(float))

如果我更改我的类定义以定义函数外部类,则可以正确工作:

template <typename T>
struct Foo
{
    static T doubleIt(T input);
};
template <typename T>
T Foo::doubleIt(T input)
{
    return input * 2;
}

我们可以使用dumpbin验证:

017 00000000 UNDEF  notype ()    External     | ?doubleIt@?$Foo@M@@SAMM@Z (public: static float __cdecl Foo<float>::doubleIt(float))

问题使用该解决方案是在类定义之外移动所有函数定义是很多键入的,尤其是当您获得更多模板参数时。

我已经尝试使用dectspec(__ noinline(,但它仍然无法正确外部功能(并在可能的情况下阻止函数的嵌入方式(。

有效的一件事是像这样列举每个功能,但这当然更繁琐:

extern template int Foo<int>::doubleIt(int);
extern template float Foo<float>::doubleIt(float);
extern template double Foo<double>::doubleIt(double);

我想要的是一种将功能定义保持在类定义内部的函数定义的方法,同时仍允许在可能的情况下将函数列入函数,但是当它不嵌入时,仅在其明确的汇编单元中创建它实例化(换句话说,与在类定义外移动功能完全相同的行为(。

您不能双向采用这两种方式,以使编译器需要使用源代码的方法,因为该方法被定义为内联,编译器不会将其编译为如果对象文件未直接在该对象中使用(即使是在任何情况下是在对象中嵌套的,则它都不会以单独的方法存在在对象中(。如果编译器在标题中定义,则始终必须构建函数,以某种方式强迫编译器将该函数的副本存储在对象文件中。

正如指出的那样,您不能同时具有extern和内线,但是关于额外的打字部分,我做了类似的事情,并试图使用预处理器将其最小化。我不确定您是否会觉得这有用,但是以防万一,我将在其中包含一个模板函数的示例。

文件Foo.h

template<typename T1>
struct Foo
{
    void bar(T1 input)
    {
        // ...
    }
    template<typename T2>
    void baz(T1 input1, T2 input2);
};
#include <Foo.inl>

文件Foo.cc

template<typename T1>
template<typename T2>
void Foo<T1>::baz(T1 input1, T2 input2)
{
    // ...
}
#define __FOO_IMPL
#include <Foo.inl>
#undef __FOO_IMPL

文件Foo.inl

#ifdef __FOO_IMPL
#define __FOO_EXTERN
#else
#define __FOO_EXTERN extern
#endif
#define __FOO_BAZ_INST(T1, T2) 
    __FOO_EXTERN template void Foo<T1>::baz<T2>(T1, T2);
#define __FOO_INST(T1) 
    __FOO_EXTERN template struct Foo<T1>; 
    __FOO_BAZ_INST(T1, int) 
    __FOO_BAZ_INST(T1, float) 
    __FOO_BAZ_INST(T1, double) 
__FOO_INST(int)
__FOO_INST(float)
__FOO_INST(double)
#undef __FOO_INST
#undef __FOO_BAZ_INST
#undef __FOO_EXTERN

因此,它仍然是很多写作,但是至少您不必小心地与不同的模板声明保持同步,也不必明确地浏览所有可能的类型组合。就我而言,我有一个具有两个类型参数的类模板,并带有一个带有额外类型参数的成员函数模板,并且每种都可以占用12种可能的类型。36行比12 3 = 1728更好,尽管我希望预处理程序更喜欢通过每个参数的类型列表以某种方式迭代,但是无法弄清楚如何。

作为旁注,就我而言,我正在编译一个需要编译所有模板的DLL,因此实际上模板的实例化/声明看起来更像__FOO_EXTERN template __FOO_API ...