3

总结
这个问题是关于在几个不同的翻译单元中实现单个模板类实例化的单独编译。

问题
对于非模板类,可以将定义放在多个 .cpp 文件中并分别编译。例如:

档案啊:

class A {
public:
  void func1();
  void func2();
  void func3() { /* defined in class declaration */}
}

文件 A1.cpp:

void A::func1() { /* do smth */ }

文件 A2.cpp:

void A::func2() { /* do smth else */ }

现在我尝试对模板类做类似的事情。因为我确切地知道我需要哪些实例,所以我明确地实例化了模板。我正在分别编译每个实例化,因为成员函数包含相当大的数学表达式,这可能会在高优化级别上大大降低编译器的速度。所以我尝试了以下方法:

文件 TA.h:

template <typename T>
class TA {
public:
  void func1();
  void func2();
  void func3() { /* defined in class declaration */}
}

文件 TA1.cpp:

template <typename T>
void TA<T>::func1() { /* do smth */ }
template class TA<sometype>;

文件 TA2.cpp:

template <typename T>
void TA<T>::func2() { /* do smth else */ }
template class TA<sometype>;

它适用于 Linux 上的 clang 和 GCC,但在重复符号错误期间链接期间在 Mac 上使用 GCC 失败(在此示例中,由于 func3,它在 TA1.cpp 和 TA2.cpp 中都已实例化)。

然后我偶然发现了标准中的这句话:

C++11.14.7,第 5 段:
对于给定的模板和给定的模板参数集,
-- 显式实例化定义在程序中最多出现一次,
-- ...

这是否意味着即使使用显式实例化也不可能(不允许)单独编译模板类(隐式实例化显然不可能)?

PS我不在乎,因为我已经得到了答案,但是无论谁认为它都在这里得到了回答https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-头文件错误。

4

2 回答 2

2

再看标准后,在我看来,唯一合理的选择是使用单个显式模板类实例化与少量“困难”函数的显式成员函数实例化相结合。

这(根据 14.7.2p9)将实例化类和到目前为止已定义的所有成员(应包括“困难”成员之外的所有内容)。然后这些选定的成员可以在包含其定义的其他翻译单元中显式实例化。

这将使我的示例如下所示(假设TA1.cpp包含简单的函数,并且TA中唯一的“困难”函数是func2

文件 TA1.cpp:

template <typename T>
void TA<T>::func1() { /* "simple" function definition */ }

template class TA<sometype>; /* expl. inst. of class */

文件 TA2.cpp:

template <typename T>
void TA<T>::func2() { /* "difficult" function definition */ }

template void TA<sometype>::func2(); /* expl. inst. of member */

这种方法要求我们为每个“难”的函数编写显式的实例化定义,这很乏味,但也让我们三思而后行,是否真的要单独保留它。

免责声明

什么时候有用?不经常。正如这里的其他人所提到的,不建议将类的定义拆分到多个文件中。在我的特殊情况下,“困难”函数包含对非平凡类实例的复杂数学运算。C++ 模板并不以编译速度快而闻名,但在这种情况下,它是难以忍受的。这些函数相互调用,这使编译器在扩展/内联重载运算符/模板/等的漫长而消耗内存的过程中优化它看到的所有内容,几乎为零改进,但使编译持续数小时。这种将某些函数隔离在单独文件中的技巧将编译速度提高了 20 倍(并且还允许并行化它)。

于 2014-02-04T14:50:35.580 回答
1

模板的单独编译很棘手,但允许。您不能做的是在多个翻译单元中显式实例化类型,就像您不能在两个翻译单元中定义相同的函数一样。

于 2014-02-03T18:23:09.660 回答