单独编译和模板显式实例化

时间:2014-02-03 18:07:47

标签: c++ templates explicit-instantiation

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

问题
对于非模板类,可以将定义放在几个.cpp文件中并单独编译它们。例如:

档案A.h:

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-header-file是错误的。

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

在看了标准之后,在我看来,唯一合理的选择是使用单个显式模板类实例化与少数&#34;困难&#34;的显式成员函数实例化相结合。功能

这(根据14.7.2p9)将实例化该类以及到目前为止已定义的所有成员(其中应包括&#34;困难&#34;成员之外的所有内容)。然后,可以在包含其定义的其他翻译单元中明确地实例化这些选定的成员。

这将使我的示例看起来如下(假设 TA1.cpp 包含简单的函数,并且 TA 中唯一的&#34;困难&#34;函数是<强> 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 */

这种方法要求我们为每个&#34;困难&#34;写出明确的实例化定义。功能,这是乏味的,但也让我们三思而后行,我们是否真的想要单独保留它。

<强>声明

什么时候有用?不经常。正如其他人提到的那样,不建议将类的定义分成几个文件。在我的特殊情况下&#34;困难&#34;函数包含对非平凡类的实例的复杂数学运算。 C ++模板并不以快速编译速度而闻名,但在这种情况下它是无法忍受的。这些函数相互调用,它们在长时间和内存消耗的过程中发送编译器来扩展/内联重载的运算符/模板/等以优化它看到的所有内容,几乎没有任何改进,但是编译持续数小时。这种在单独文件中隔离某些函数的技巧可以加速编译20次(并允许并行化)。