模板:使用前向声明来减少编译时间?
我必须处理一个由许多模板化类组成的库,这些类当然都是在头文件中实现的.现在我正试图找到一种方法来减少无法忍受的长编译时间,因为我几乎必须在我的每个编译单元中都包含整个库.
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.
尽管有模板,是否可以使用前向声明?我正在尝试按照以下示例的方式进行操作,我试图绕过 #include <vector>
,作为示例,但它给了我一个链接器错误,因为 push_back
未定义.
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.
更新: 有些人说预先声明 STL 类是不值得的.我要强调的是,上面的 STL vector
只是一个例子.我并不是真的想提前声明 STL 类,而是关于我必须使用的某个库的其他高度模板化的类.
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.
更新 2: 有没有办法让上面的例子正确编译和链接?Logan 建议使用 -fno-implicit-templates
并将 template class std::vector
放在某处,大概是一个单独的 .cpp
使用 -fno-implicit-templates
编译的文件,但我仍然收到链接器错误.同样,我试图了解它是如何用于 std::vector
的,以便我可以将它应用到我实际使用的模板化类中.
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.
推荐答案
您不能像这样转发声明类的部分".即使可以,您仍然需要在某处实例化代码,以便您可以链接它.有很多方法可以处理它,你可以让自己成为一个带有常见容器(例如向量)实例化的小库并将它们链接起来.然后你只需要编译,例如向量-fno-implicit-templates
的东西,至少假设您坚持使用 g++ 并使用 template class std:: 在您的库中显式实例化模板:矢量
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>
所以,一个真实的工作示例.这里我有 2 个文件,a.cp??p 和 b.cpp
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;
}
所以现在我可以用 -fno-implicit-templates
编译 a.cpp:
So now I can compile a.cpp with -fno-implicit-templates
:
g++ -fno-implicit-templates -c a.cpp
这会给我a.o.如果我然后我尝试链接 a.o 我得到:
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
不好.所以我们转向 b.cpp:
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>);
现在您要对自己说,所有这些额外的模板内容从何而来?我看到了 template class std::vector
,这很好,但剩下的呢?嗯,简短的回答是,这些东西的实现必然有点混乱,当你手动实例化它们时,通过扩展,一些混乱会泄漏出来.你可能想知道我是如何弄清楚我需要实例化什么的.好吧,我使用了链接器错误;)
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 ;).
所以现在我们编译 b.cpp
So now we compile b.cpp
g++ -fno-implicit-templates -c b.cpp
我们得到了 b.o.链接 a.o 和 b.o 我们可以得到
And we get b.o. Linking a.o and b.o we can get
g++ a.o b.o
万岁,没有链接器错误.
Hooray, no linker errors.
因此,要了解有关您更新的问题的一些详细信息,如果这是自酿课程,则不一定非得如此混乱.例如,您可以将接口与实现分开,例如假设我们有 c.h、c.cpp,除了 a.cpp 和 b.cpp
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,库":
#include "c.h" // need interface
#include "c.cpp" // need implementation to actually instantiate it
template class MyExample<int>;
现在您将 b.cpp 编译为 b.o 一次.当 a.cpp 更改时,您只需要重新编译它并在 b.o. 中链接
Now you compile b.cpp to b.o once. When a.cpp changes you just need to recompile that and link in b.o.
相关文章