模板:使用forward声明减少编译时间? [英] Templates: Use forward declarations to reduce compile time?

查看:150
本文介绍了模板:使用forward声明减少编译时间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



使用转发声明一个可能性,尽管模板?我试着沿着下面的例子,我尝试绕过 #include< vector> ,作为一个例子,但它给我一个链接器错误,因为 push_back 未定义。

  #include< iostream> 

namespace std {
template< class T>
类向量{
public:
void push_back(const T& t);
};
}

int main(int argc,char ** argv){
std :: vector< int> * vec = new std :: vector< int>
vec-> push_back(3);
delete vec;
return EXIT_SUCCESS;
}

$ g ++ fwddecl.cpp
ccuqbCmp.o(.text + 0x140):在函数main中:
:undefined引用`std :: vector< int> :: push_back(int const&)'
collect2:ld返回1退出状态

我试过预编译头一次,但这并没有改变编译时间(我确实确保它们确实加载,而不是真正的头)。



UPDATE:有些人说不值得转发声明STL类。我应该强调,上面的STL 向量只是一个例子。我不是真的想转发声明STL类,但它是关于其他的,非常模板的类的一些库,我必须使用。



更新2 :有没有办法使上面的例子实际编译和链接正确? Logan建议使用 -fno-implicit-templates 并将模板类std :: vector< int> 变成用 -fno-implicit-templates 编译的单独的 .cpp 文件,但是我仍然得到链接器错误。再次,我试图了解它如何工作 std :: vector ,这样我可以应用它到我实际使用的模板类。

解决方案

您不能转发声明类的parts。即使你可以,你仍然需要实例化的代码在某个地方,所以你可以链接到它。有办法来处理它,你可以使自己成为一个小的库实例化的通用容器(例如矢量),并链接到它们。然后你只需要编译例如。 vector< int>一旦。要实现这个,你需要使用像 -fno-implicit-templates ,至少假设你坚持用g ++和显式实例化模板在你的lib与模板类std :: vector< int>






工作示例。这里有两个文件a.cpp和b.cpp



a.cpp:

  #include< vector> //仍然需要知道接口
#include< cstdlib>

int main(int argc,char ** argv){
std :: vector< int> * vec = new std :: vector< int>
vec-> push_back(3);
delete vec;
return EXIT_SUCCESS;
}



现在我可以使用 fno-implicit-templates

  g ++ -fno-implicit-templates -c a.cpp 

这会给我一个如果我然后我尝试链接ao我获得:

  g ++ ao 
/ usr / bin / ld: :
std :: vector< int,std :: allocator< int> > :: _ M_insert_aux(__ gnu_cxx :: __ normal_iterator< int *,std :: vector< int,std :: allocator< int>>>,int const&)
void std :: _ Destroy< int * std :: allocator< int> >(int *,int *,std :: allocator< int>)
collect2:ld返回1退出状态

没有好。所以我们转向b.cpp:

  #include< vector> 
template class std :: vector< int> ;;
template void std :: _ Destroy(int *,int *,std :: allocator< int>);
template void std :: __ uninitialized_fill_n_a(__ gnu_cxx :: __ normal_iterator< int *,std :: vector< int,std :: allocator< int>> ;, unsigned long,int const& std :: allocator< int>);
template void std :: __ uninitialized_fill_n_a(int *,unsigned long,int const& std :: allocator< int>);
template void std :: fill(__ gnu_cxx :: __ normal_iterator< int *,std :: vector< int,std :: allocator< int>>>,__gnu_cxx :: __ normal_iterator< int *,std :: vector< ; int,std :: allocator< int>>>,int const&);
template __gnu_cxx :: __ normal_iterator< int *,std :: vector< int,std :: allocator< int> > > std :: fill_n(__ gnu_cxx :: __ normal_iterator< int *,std :: vector< int,std :: allocator< int>> ;, unsigned long,int const&
template int * std :: fill_n(int *,unsigned long,int const&);
template void std :: _ Destroy(__ gnu_cxx :: __ normal_iterator< int *,std :: vector< int,std :: allocator< int>>> ;, __gnu_cxx :: __ normal_iterator< int *,std :: vector< ; int,std :: allocator< int>>>,std :: allocator< int>);

现在你对自己说,这些额外的模板事情从何而来?我看到模板类std :: vector< int> 这很好,但其余的呢?好的简单的答案是,这些东西实现必然有点凌乱,当你手动实例化它们,通过扩展一些这种凌乱泄漏出来。你可能想知道我是怎么想出我需要实例化的东西。现在我们编译b.cpp

 

code> g ++ -fno-implicit-templates -c b.cpp

链接ao和bo我们可以获得

  g ++ ao bo 

Hooray,没有链接器错误。



因此,要了解有关更新问题的一些详细信息,家庭酿造的类不一定是这个混乱。例如,您可以将接口与实现分开,例如。说我们有ch,c.cpp,除a.cpp和b.cpp之外



ch

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

c.cpp

  template< typename T> 
MyExample< T> :: MyExample(const T& t):m_t(t){}
template< typename T&
T MyExample< T> :: get(){return m_t; }
template< typename T>
void MyExample< T> :: set(const T& t){m_t = t; }

a.cpp

  #includech//只需要接口
#include< iostream>
int main(){
MyExample< int> x(10);
std :: cout<< x.get()< std :: endl;
x.set(9);
std :: cout<< x.get()<< std :: endl;
return EXIT_SUCCESS;
}

b.cpp,library:

  #includech//需要接口
#includec.cpp//需要实现实例化
模板class MyExample< int> ;;

现在,将b.cpp编译为b.o一次。当a.cpp更改时,您只需要重新编译并链接到b.o。


I have to deal with a library that consists of many templated classes, which are of course all implemented in header files. Now I'm trying to find a way to reduce the unbearably long compile times that come from the fact that I pretty much have to include the whole library in each and one of my compilation units.

Is using forward declarations a possibility, despite the templates? I'm trying something along the lines of the example below, where I attempted to get around the #include <vector>, as an example, but it's giving me a linker error because push_back is undefined.

#include <iostream>

namespace std {
  template<class T>
  class vector {
  public:
    void push_back(const T& t);
  };
}

int main(int argc, char** argv) {
  std::vector<int>* vec = new std::vector<int>();
  vec->push_back(3);
  delete vec;
  return EXIT_SUCCESS;
}

$ g++ fwddecl.cpp
ccuqbCmp.o(.text+0x140): In function `main':
: undefined reference to `std::vector<int>::push_back(int const&)'
collect2: ld returned 1 exit status

I tried precompiled headers once but that didn't change the compile times at all (I did make sure they were indeed loaded instead of the real headers). But if you all say that precompiled headers should be the way to go then I'll give that a try again.

UPDATE: Some people say it's not worth to forward-declare the STL classes. I should stress that the STL vector above was just an example. I'm not really trying to forward-declare STL classes, but it's about other, heavily templated classes of some library that I have to use.

UPDATE 2: Is there a way to make above example actually compile and link properly? Logan suggests to use -fno-implicit-templates and put template class std::vector<int> somewhere, presumably into a separate .cpp file that gets compiled with -fno-implicit-templates, but I still get linker errors. Again, I'm trying to understand how it works for std::vector so that I can then apply it to the templated classes that I'm actually using.

解决方案

You can't forward declare "parts" of classes like that. Even if you could, you'd still need to instantiate the code somewhere so you could link against it. There are ways to handle it, you could make yourself a little library with instantiations of common containers (e.g. vector) and link them in. Then you'd only ever need to compile e.g. vector<int> once. To implement this you'll need to use something like -fno-implicit-templates, at least assuming you are sticking with g++ and explicitly instantiate the template in your lib with template class std::vector<int>


So, a real working example. Here I have 2 files, a.cpp and b.cpp

a.cpp:

#include <vector> // still need to know the interface
#include <cstdlib>

int main(int argc, char **argv) {
  std::vector<int>* vec = new std::vector<int>();
  vec->push_back(3);
  delete vec;
  return EXIT_SUCCESS;
}

So now I can compile a.cpp with -fno-implicit-templates:

g++ -fno-implicit-templates -c a.cpp

This will give me a.o. If I then I try to link a.o I get:

g++ a.o
/usr/bin/ld: Undefined symbols:
std::vector<int, std::allocator<int> >::_M_insert_aux(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&)
void std::_Destroy<int*, std::allocator<int> >(int*, int*, std::allocator<int>)
collect2: ld returned 1 exit status

No good. So we turn to b.cpp:

#include <vector>
template class std::vector<int>;
template void std::_Destroy(int*,int*, std::allocator<int>);
template void std::__uninitialized_fill_n_a(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, unsigned long, int const&, std::allocator<int>);
template void std::__uninitialized_fill_n_a(int*, unsigned long, int const&, std::allocator<int>);
template void std::fill(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&);
template __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::fill_n(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, unsigned long, int const&);
template int* std::fill_n(int*, unsigned long, int const&);
template void std::_Destroy(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, std::allocator<int>);

Now you're saying to yourself, where did all these extra template things come from? I see the template class std::vector<int> and that's fine, but what about the rest of it? Well the short answer is that, these things implementations are by necessity a little messy, and when you manually instantiate them, by extension some of this messiness leaks out. You're probably wondering how I even figured out what I needed to instantiate. Well I used the linker errors ;).

So now we compile b.cpp

g++ -fno-implicit-templates -c b.cpp

And we get b.o. Linking a.o and b.o we can get

g++ a.o b.o

Hooray, no linker errors.

So, to get into some details about your updated question, if this is a home brewed class it doesn't necessarily have to be this messy. For instance, you can separate the interface from the implementation, e.g. say we have c.h, c.cpp, in addition to a.cpp and b.cpp

c.h

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

c.cpp

template<typename T>
MyExample<T>::MyExample(const T& t) : m_t(t) {}
template<typename T>
T MyExample<T>::get() { return m_t; }
template<typename T>
void MyExample<T>::set(const T& t) { m_t = t; }

a.cpp

 #include "c.h" // only need interface
 #include <iostream>
 int main() {
   MyExample<int> x(10);
   std::cout << x.get() << std::endl;
   x.set( 9 );
   std::cout << x.get() << std::endl;
   return EXIT_SUCCESS;
 }

b.cpp, the "library":

 #include "c.h" // need interface
 #include "c.cpp" // need implementation to actually instantiate it
 template class MyExample<int>;

Now you compile b.cpp to b.o once. When a.cpp changes you just need to recompile that and link in b.o.

这篇关于模板:使用forward声明减少编译时间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆