当前位置:  开发笔记 > 编程语言 > 正文

模板:使用前向声明来减少编译时间?

如何解决《模板:使用前向声明来减少编译时间?》经验,为你挑选了3个好方法。

我必须处理一个由许多模板化类组成的库,这些类当然都是在头文件中实现的.现在我正试图找到一种方法来减少无法忍受的长编译时间,因为我几乎必须在每个编译单元中包含整个库.

尽管有模板,使用前向声明是否可能?我正在尝试下面的例子中的一些东西,我尝试绕过它#include ,作为一个例子,但它给了我一个链接器错误,因为push_back未定义.

#include 

namespace 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以便我可以将它应用于我实际使用的模板化类.



1> Logan Capald..:

你不能转发声明类的"部分".即使你可以,你仍然需要在某处实例化代码,以便你可以链接它.有办法处理它,你可以让自己成为一个带有常见容器实例的小库(例如vector)并将它们链接起来.然后你只需要编译例如vector 一次.要实现这一点,你需要使用类似的东西-fno-implicit-templates,至少假设你坚持使用g ++并在lib中显式实例化模板template class std::vector


所以,一个真实的例子.这里我有2个文件,a.cp​​p和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:

#include 
template 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

template
class MyExample {
  T m_t;
  MyExample(const T& t);
  T get();
  void set(const T& t);
};

c.cpp

template
MyExample::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
 #include 
 int 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中链接



2> Josh Kelley..:

前向声明允许您这样做:

template  class vector;

然后你可以声明对引用和指针vector而不定义向量(不包括vector's头文件).这与常规(非模板)类的前向声明相同.

特别是模板的问题在于,您通常不仅需要类声明,还需要头文件中的所有方法定义(以便编译器可以实例化所需的模板).显式模板实例化(您可以强制使用with -fno-implicit-templates)是一种解决方法; 您可以将方法定义放在源文件中(或者,按照Google样式指南的示例,在-inl.h您不必包含的头文件中)然后显式实例化它们,如下所示:

template  class 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(调用以查看正在读取的文件.)


你提出了一个很好的观点-fno-implicit-templates.当问题是编译速度时,我对"转发声明"模板的想法感到分心.

3> gix..:

使用前向声明,您只能将成员或参数声明为指向该类型的指针或引用.您不能使用任何需要所述类型内部的方法或其他东西.这就是说我发现前向声明在尝试加快编译时间时确实存在限制.我建议你研究一下预编译头文件的可能性,因为我发现它们确实有助于编译时间,尽管这是在Windows上使用Visual C++而不是g ++.

推荐阅读
手机用户2502851955
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有