2009-09-29 60 views
3

它曾经是在C++中使用模板类的,实现必须在头文件中,或者#included到底部的头文件中。C++模板:头文件仍然破碎?

我几年没用过C++模板;我刚开始再次使用它们,并观察到此行为似乎持续存在。这仍然是这样吗?或者编译器现在是否足够聪明以便将实现与接口分开?

+0

即使出口,你仍然不会避免不释放源代码(以任何形式)。 EDG文档说:“.et文件只告诉前端关于导出模板定义的位置;它们实际上不包含这些定义。因此,包含导出模板定义的源必须在实例化时可用(通常,特别是出口工具不是一种避免以源格式发布模板定义的机制。*“(由我强调) – 2009-09-29 13:57:07

+0

@litb:这太让人生气了。但是,谢谢你的回答。 – 2009-09-29 13:57:53

+0

我实际上没有实现一个编译器,所以我没有这样的说法,但我相信EDG的创造者:) – 2009-09-29 14:29:47

回答

2

要将执行与声明分开,标准强制您使用export关键字。据我所知,只有一个编译器知道如何处理它:Comeau。

但是,C++ 0x将包含一个机制,告诉编译器不要自动实例化某些特化(extern模板)。所以,如果你想缩短编译时间,你可以通过在一个编译单元中明确地实例化一些特化并在头文件中将它们声明为extern来实现。

2

您指的是导出的模板(使用export关键字),这似乎仅由Comeau C++支持(根据C++ FAQ Litethis section)。

保持界面没有实现代码的常用技术是将内联函数定义放入一个单独的“实现”头中,该头可以包含在声明头的末尾。

+0

说实话,我本来不是在寻找出口。我只是在寻找标题,而不必在其中包含模板实现(像其他语言功能一样)。 – 2009-09-30 00:06:37

1

导出只支持EDG前端,商业上只有在Comeau编译器中可用,据我所知。

导出不会消除源泄露的需要,也不会减少编译依赖性,同时它需要编译器构建者的大量工作。

所以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 

这只涨幅比较清楚,当然,没有什么变化相比,只是在一个头文件内联的一切。

+0

找到您参考的pdf:http://ra.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1426.pdf,日期为2003年3月 – 2009-09-29 15:42:29

+0

@Matthieu M.:PDF似乎已损坏。最好拿到原文:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1426.pdf – paercebal 2011-12-26 17:46:28

+0

@paercebal:谢谢! – 2011-12-27 17:16:56

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 < int>的或X <浮>等。但abovr类是完全支持定义。

我也看到这种技术用于减少编译时间。因为每个编译单元都不会重新生成相同版本的X,所以我们只能在一个地方得到定义(因此一个编译成本)。缩小到这个是你必须手动instanciate你使用的每个分离版本的X.

+0

这很漂亮,虽然它没有做我所需要的。不过,我会记住它的。 – 2009-09-30 00:02:22

0

除非您明确实例化所有模板,否则GCC会经历漫长的收集阶段。 VC++似乎应付,但我宁愿避免这一步,并在我知道如何使用模板的情况下,这通常是应用程序的情况,而不是库,我把模板定义放到一个单独的文件中。这也使得代码更加可读,使得声明更少地与实现细节混杂在一起。