将源文件用于基于模板的类(STL和boost)以及将实现放入标头似乎是一种常见的惯例。我假设与头文件和源文件中的声明和实现之间的经典分离相比,这将增加编译包含头文件的源文件所需的时间。 The reason why this is done is probably due to the fact that you would have to tell the compiler in the source file which templates to use, which will probably result in a bloated .a file.
假设链接器在库增长时也需要更多时间,那么编译包含库头的源文件所需的时间会更快?
1。不使用.cpp文件并将整个类(包括实现)放入标题
//foo.hpp
template <class T>
class Foo
{
public:
Foo(){};
T bar()
{
T* t = NULL;
//do stuff
return *t;
}
};
或
2。在库本身的源文件中显式编译各种类型的模板
//foo.h
template <class T>
class Foo
{
public:
Foo(){};
T bar();
};
//foo.cpp
template <class T>
T Foo<T>::bar()
{
T* t = NULL;
//do stuff
return *t;
}
template class Foo<int>;
template class Foo<float>;
template class Foo<double>;
template class Foo<long long>;
答案 0 :(得分:4)
通常,编译器为特定的编译单元生成一次代码,然后链接器确保其他编译单元可以访问变量和函数。
说到模板,不再是这种情况了。在实例化模板的具体实例之前,编译器无法为模板生成代码。因此,我们在每个编译单元中实例化模板,没有复制和粘贴的唯一方法是将所有模板代码放在头文件中。
然后,链接器还必须是模板识别并协调同一对象的多个实例。
C ++ 11对这种情况有所帮助,可以声明:
template class MyTemplate<MyType>;
在单个C ++文件中,然后使用:
extern template class MyTemplate<MyType>;
后者不会在当前编译单元中实例化模板,但会使链接器链接到已定义的模板。
请参阅here了解更多详情
答案 1 :(得分:4)
模板的关键问题是编译器不知道将使用哪个模板参数模板。编译器知道模板与唯一一组参数一起使用的唯一时间是它看到使用的模板时,编译器将在此时实例化模板。因此,代码通常被放入标题中,因此编译器可以在使用时代表用户实例化模板。
或者,模板的作者可以告诉编译器模板与特定的模板参数列表一起使用,并且只是显式地实例化它们。在这种情况下,模板定义可以进入源文件(或者更可能是用户通常不包括的特殊标头)。这种方法的问题在于模板代码的作者不一定知道需要哪些实例化。
在C ++ 2011中,还有一个中间立场:通过将专业化声明为extern
,可以告诉编译器已经创建了某些实例化。这样,编译器知道它不需要使用某些参数实例化模板,但如果使用其他参数,它知道它需要创建它们。例如,标准C ++库有std::basic_string
,它可以预测可能会使用char
和wchar_t
的实例化,并可以将它们放入库中,将实例化声明为{{ 1}}。但是,让代码随时可用使得extern
与用户定义的类型一起使用是可行的。
展望未来我们希望得到一个模块系统,但现在没有人真正知道这样一个系统应该如何工作。对该主题感兴趣的编译器实现者有一个组来考虑模块,这样的系统很可能有助于模板的编译时间。
答案 2 :(得分:1)
已经有两个很好的答案了,所以我将简要地写一下
没有具体类型,无法编译模板化代码,这就是为什么你不能做经典的“编译和链接”。
模板函数或类不完整,在某种意义上并非所有类型都被解析,直到它在代码中的特定用法(某种类型替换模板)。
因此,模板放在头文件中,无法独立于特定用途进行编译。