我必须处理一个由许多模板化类组成的库,这些类当然都是在头文件中实现的.现在我正试图找到一种方法来减少无法忍受的长编译时间,因为我几乎必须在每个编译单元中包含整个库.
尽管有模板,使用前向声明是否可能?我正在尝试下面的例子中的一些东西,我尝试绕过它#include
,作为一个例子,但它给了我一个链接器错误,因为push_back
未定义.
#includenamespace std { template class vector { public: void push_back(const T& t); }; } int main(int argc, char** argv) { std::vector * vec = new std::vector (); vec->push_back(3); delete vec; return EXIT_SUCCESS; } $ g++ fwddecl.cpp ccuqbCmp.o(.text+0x140): In function `main': : undefined reference to `std::vector ::push_back(int const&)' collect2: ld returned 1 exit status
我尝试了一次预编译的头文件但是根本没有改变编译时间(我确实确实加载了它们而不是真正的头文件).但是,如果你们都说预编译头应该是可行的方式,那么我将再试一次.
更新:有些人说,转发声明STL类是不值得的.我应该强调,vector
上面的STL 只是一个例子.我并没有真正尝试向前声明STL类,但它是关于我必须使用的某些库的其他严格模板化的类.
更新2:有没有办法使上面的例子实际编译和链接正确?Logan建议使用-fno-implicit-templates
并放在template class std::vector
某个地方,大概是一个.cpp
可以编译的单独文件-fno-implicit-templates
,但我仍然会遇到链接器错误.再次,我试图理解它是如何工作的,std::vector
以便我可以将它应用于我实际使用的模板化类.
你不能转发声明类的"部分".即使你可以,你仍然需要在某处实例化代码,以便你可以链接它.有办法处理它,你可以让自己成为一个带有常见容器实例的小库(例如vector)并将它们链接起来.然后你只需要编译例如vector -fno-implicit-templates
,至少假设你坚持使用g ++并在lib中显式实例化模板template class std::vector
所以,一个真实的例子.这里我有2个文件,a.cpp和b.cpp
a.cpp:
#include// still need to know the interface #include int main(int argc, char **argv) { std::vector * vec = new std::vector (); vec->push_back(3); delete vec; return EXIT_SUCCESS; }
所以现在我可以编译a.cpp -fno-implicit-templates
:
g++ -fno-implicit-templates -c a.cpp
这将给我一个如果我然后我尝试链接我得到:
g++ a.o /usr/bin/ld: Undefined symbols: std::vector>::_M_insert_aux(__gnu_cxx::__normal_iterator > >, int const&) void std::_Destroy >(int*, int*, std::allocator ) collect2: ld returned 1 exit status
不好.所以我们转向b.cpp:
#includetemplate class std::vector ; template void std::_Destroy(int*,int*, std::allocator ); template void std::__uninitialized_fill_n_a(__gnu_cxx::__normal_iterator > >, unsigned long, int const&, std::allocator ); template void std::__uninitialized_fill_n_a(int*, unsigned long, int const&, std::allocator ); template void std::fill(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, int const&); template __gnu_cxx::__normal_iterator > > std::fill_n(__gnu_cxx::__normal_iterator > >, unsigned long, int const&); template int* std::fill_n(int*, unsigned long, int const&); template void std::_Destroy(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator );
现在你要对自己说,所有这些额外的模板都来自哪里?我看到了template class std::vector
那很好,但剩下的呢?简而言之,这些实现必然有点混乱,当你手动实例化它们时,通过扩展,一些混乱泄漏了.你可能想知道我是怎么想出我需要实例化的东西.好吧,我使用了链接器错误;).
所以现在我们编译b.cpp
g++ -fno-implicit-templates -c b.cpp
我们得到了联系我们和我们可以得到的bo
g++ a.o b.o
万岁,没有链接器错误.
因此,要了解有关您更新的问题的一些细节,如果这是一个自制的课程,它不一定非常混乱.例如,您可以将接口与实现分开,例如,除了a.cpp和b.cpp之外,我们还有ch,c.cpp.
CH
templateclass MyExample { T m_t; MyExample(const T& t); T get(); void set(const T& t); };
c.cpp
templateMyExample ::MyExample(const T& t) : m_t(t) {} template T MyExample ::get() { return m_t; } template void MyExample ::set(const T& t) { m_t = t; }
a.cpp
#include "c.h" // only need interface #includeint main() { MyExample x(10); std::cout << x.get() << std::endl; x.set( 9 ); std::cout << x.get() << std::endl; return EXIT_SUCCESS; }
b.cpp,"图书馆":
#include "c.h" // need interface #include "c.cpp" // need implementation to actually instantiate it template class MyExample;
现在你将b.cpp编译为bo一次.当a.cpp改变时,你只需要重新编译它并在bo中链接
前向声明允许您这样做:
templateclass vector;
然后你可以声明对引用和指针vector
而不定义向量(不包括vector
's头文件).这与常规(非模板)类的前向声明相同.
特别是模板的问题在于,您通常不仅需要类声明,还需要头文件中的所有方法定义(以便编译器可以实例化所需的模板).显式模板实例化(您可以强制使用with -fno-implicit-templates
)是一种解决方法; 您可以将方法定义放在源文件中(或者,按照Google样式指南的示例,在-inl.h
您不必包含的头文件中)然后显式实例化它们,如下所示:
templateclass vector;
请注意,您实际上并不需要-fno-implicit-templates
从中受益; 编译器将默默地避免实例化任何没有定义的模板,假设链接器稍后会解决它.添加-fno-implicit-templates
将使所有模板更难使用(不仅仅是耗时的模板),所以我不推荐它.
您的示例代码的问题在于您没有向前声明真正的std::vector
类.通过不包括
,你正在创建自己的非标准vector
类,并且你没有定义push_back
,所以编译器没有任何实例化.
我使用预编译的头文件效果很好; 我不确定他们为什么不帮助你.您将所有未更改的标头放在一个all.h
预编译的单个中,并使用已加载的strace
或类似的已验证的all.h.pch
单个头文件进行验证?(如何使用strace
:而不是运行g++ mytest.cc
,运行strace -o strace.out g++ mytest.cc
,然后strace.out
在文本编辑器中查看并搜索open(
调用以查看正在读取的文件.)
使用前向声明,您只能将成员或参数声明为指向该类型的指针或引用.您不能使用任何需要所述类型内部的方法或其他东西.这就是说我发现前向声明在尝试加快编译时间时确实存在限制.我建议你研究一下预编译头文件的可能性,因为我发现它们确实有助于编译时间,尽管这是在Windows上使用Visual C++而不是g ++.