以前在C ++中使用模板类,实现必须在头文件中,或者#included到底部的头文件中。
我几年没用过C ++模板了;我刚刚开始再次使用它们,并观察到这种行为似乎持续存在。这仍然是这样吗?或者编译器现在是否足够聪明,以便将实现与接口分开?
答案 0 :(得分:3)
从技术上讲,他们不会需要在头文件中。
这种用法的一个例子是当你有一个带有一组固定版本的模板类时(假设参数为char和wchar_t)。然后,您可以将所有方法的延迟放入源文件中,并显式实现这两个版本。这具有安全性,其他人不能将模板用于不应该用于的类型。
// X.h
template<typename T>
class X
{
// DECLARATION ONLY OF STUFF
public:
X(T const& t);
private:
T m_t;
};
// X.cpp
#include "X.h"
// DEFINTION OF STUFF
template<typename T>
X<T>::X(T const& t)
:m_t(t)
{}
// INSTANCIATE The versions you want.
template class X<char>;
template class X<wchar_t>;
// Main.cpp
#include "X.h"
int main()
{
X<chat> x1('a');
X<wchar_t> x2(L'A');
// X<int> x3(5); // Uncomment for a linker failure.
}
假设人们不能直接包含X.cpp(因为它不是由发行版提供),那么其他人不能使用X&lt; int&gt;或X&lt; float&gt;但是abovr类是完全定义的。
我也看到这种技术用于减少编译时间。因为每个编译单元不会重新生成相同版本的X,所以我们只能在一个地方获得定义(因此一个编译成本)。缩小尺寸是您必须手动设置您使用的每个单独版本的X.
答案 1 :(得分:2)
要将实现与声明分开,标准会强制您使用export关键字。据我所知,只有一个编译器知道如何处理它:Comeau。
但是,C ++ 0x将包含一种机制,告诉编译器不要自动实例化某些特化(外部模板)。因此,如果你想减少编译时间,你可以通过在一个编译单元中显式实例化一些特化并在标题中将它们声明为extern来实现。
答案 2 :(得分:2)
您指的是导出的模板(使用export
关键字),这似乎只受Comeau C ++支持(根据this section的C++ FAQ Lite)。
保持接口缺少实现代码的常用技术是将内联函数定义放入一个单独的“实现”头中,该头可以包含在声明头的末尾。
答案 3 :(得分:1)
导出仅由EDG前端支持,据我所知,仅在Comeau编译器中可用。
Export不会消除源代码泄露的需要,也不会减少编译依赖性,同时需要编译器构建器的大量工作。
因此Herb Sutter本人要求编译器构建者'忘记'出口。由于所需的时间投资会更好地花在其他地方......所以我不认为出口会在他们看到需要多长时间之后由其他编制者实施,并且获得的数量很少。
这篇论文被称为“为什么我们买不起出口”,它列在Sutters blog但没有pdf(快速谷歌应该把它翻过来),现在已经六岁了,我想他们都是倾听,从不打扰:)
许多人使用两个头文件(例如.hpp
和.ipp
),一个只有声明,一个带有定义,然后只是将一个包含在另一个中。
foo.hpp
#ifndef MY_TEMPLATES_HPP
#define MY_TEMPLATES_HPP
template< class T >
void foo(T & t);
#include "foo.ipp"
#endif
foo.ipp
#ifdef MY_TEMPLATES_IPP
nonsense here, that will generate compiler error
#else
#define MY_TEMPLATES_IPP
template< class T >
void foo(T & t) {
... // long function
}
#endif
当然,这只会获得一些清晰度,与简单地在一个头文件中内联所有内容相比,没有任何改变。
答案 4 :(得分:0)
GCC会经历一个冗长的收集阶段,除非您明确地实例化所有模板。 VC ++似乎应对,但我更倾向于避免这一步骤,并且在我知道如何使用模板的情况下,这通常是应用程序的情况,而不是库的情况,我将模板定义放入单独的文件中。通过使声明更少与实现细节混杂,这也使代码更具可读性。