C++ 模板:使用前向声明来减少编译时间?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/555330/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Templates: Use forward declarations to reduce compile time?
提问by Logan Capaldo
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 <vector>
, 作为示例,但由于push_back
未定义,它给了我一个链接器错误。
#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.
更新:有人说预先声明 STL 类是不值得的。我应该强调,vector
上面的 STL只是一个例子。我并不是真的想提前声明 STL 类,而是关于我必须使用的某个库的其他高度模板化的类。
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.
更新 2:有没有办法让上面的例子实际编译和链接正确?Logan 建议使用-fno-implicit-templates
并放置在template class std::vector<int>
某个地方,大概是一个单独的.cpp
文件,用 编译-fno-implicit-templates
,但我仍然收到链接器错误。同样,我试图了解它是如何工作的,std::vector
以便我可以将它应用于我实际使用的模板化类。
回答by Logan Capaldo
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>
你不能像这样转发声明类的“部分”。即使可以,您仍然需要在某处实例化代码,以便您可以链接它。有很多方法可以处理它,您可以让自己成为一个带有常见容器(例如向量)实例化的小库并将它们链接起来。然后您只需要编译例如 vector<int> 一次。要实现这一点,您需要使用类似的东西-fno-implicit-templates
,至少假设您坚持使用 g++ 并使用以下命令显式实例化您的库中的模板template class std::vector<int>
So, a real working example. Here I have 2 files, a.cpp and b.cpp
所以,一个真实的工作示例。这里我有 2 个文件,a.cpp 和 b.cpp
a.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
:
所以现在我可以用以下命令编译 a.cpp -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:
这会给我 ao 如果我然后我尝试链接 ao 我得到:
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:
不好。所以我们转向 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 ;).
现在你要对自己说,所有这些额外的模板东西是从哪里来的?我看到了template class std::vector<int>
,这很好,但剩下的呢?嗯,简短的回答是,这些东西的实现必然有点混乱,当你手动实例化它们时,通过扩展,一些混乱会泄漏出来。你可能想知道我是如何弄清楚我需要实例化什么的。好吧,我使用了链接器错误;)。
So now we compile b.cpp
所以现在我们编译 b.cpp
g++ -fno-implicit-templates -c b.cpp
And we get b.o. Linking a.o and b.o we can get
我们得到 bo 链接 ao 和 bo 我们可以得到
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
因此,要了解有关您更新的问题的一些详细信息,如果这是自酿课程,则不一定非得如此混乱。例如,您可以将接口与实现分开,例如,除了 a.cpp 和 b.cpp 之外,我们还有 ch、c.cpp
c.h
ch
template<typename T>
class MyExample {
T m_t;
MyExample(const T& t);
T get();
void set(const T& t);
};
c.cpp
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
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":
b.cpp,“库”:
#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.
现在您将 b.cpp 编译为 bo 一次。当 a.cpp 更改时,您只需要重新编译它并在 bo 中链接
回答by Josh Kelley
Forward declarations let you do this:
前向声明让你这样做:
template <class T> class vector;
Then you can declare references to and pointers to vector<whatever>
without defining vector (without including vector
's header file). This works the same as forward declarations of regular (non-template) classes.
然后,您可以在vector<whatever>
不定义向量的情况下声明对的引用和指针(不包括vector
头文件)。这与常规(非模板)类的前向声明相同。
The problem with templates in particular is that you usually need not just the class declaration but also all of the method definitions in your header file (so that the compiler can instantiate the needed templates). Explicit template instantiation (which you can force the use of with -fno-implicit-templates
) is a workaround for this; you can put your method definitions in a source file (or, following the example of the Google Style Guide, in a -inl.h
header file which you don't have to include) then explicitly instantiate them like this:
特别是模板的问题在于,您通常不仅需要类声明,还需要头文件中的所有方法定义(以便编译器可以实例化所需的模板)。显式模板实例化(您可以强制使用 with -fno-implicit-templates
)是一种解决方法;您可以将方法定义放在源文件中(或者,按照Google 样式指南的示例,放在-inl.h
不必包含的头文件中)然后像这样显式实例化它们:
template <class int> class vector;
Note that you don't actually need -fno-implicit-templates
to benefit from this; the compiler will silently avoid instantiating any templates it has no definitions for, on the assumption that the linker will figure it out later. And adding -fno-implicit-templates
will make using alltemplates harder (not just the time consuming ones), so I wouldn't recommend it.
请注意,您实际上并不需要-fno-implicit-templates
从中受益;编译器会默默地避免实例化它没有定义的任何模板,假设链接器稍后会解决它。添加-fno-implicit-templates
将使使用所有模板变得更加困难(不仅仅是耗时的模板),所以我不推荐它。
The problem with your example code is that you're not forward declaring the true std::vector
class. By not including <vector>
, you're creating your own, nonstandard vector
class, and you're not ever defining push_back
, so there's nothing for the compiler to instantiate.
您的示例代码的问题在于您没有提前声明真正的std::vector
类。通过不包含<vector>
,您将创建自己的非标准vector
类,并且您从未定义push_back
,因此编译器无需实例化任何内容。
I've used precompiled headers to great effect; I'm not sure why they didn't help you. You put all of your non-changing headers in a single all.h
, precompiled it, and verified with strace
or similar that all.h.pch
was loaded and individual header files were not? (How to use strace
: instead of running g++ mytest.cc
, run strace -o strace.out g++ mytest.cc
, then view strace.out
in a text editor and search for open(
calls to see which files are being read.)
我使用预编译头文件效果很好;我不知道为什么他们没有帮助你。你把你所有的不改变头在一个单一的all.h
,预编译它,并与验证strace
或类似的是all.h.pch
加载和个人的头文件没有?(如何使用strace
:而不是运行g++ mytest.cc
,运行strace -o strace.out g++ mytest.cc
,然后strace.out
在文本编辑器中查看并搜索open(
调用以查看正在读取哪些文件。)
回答by gix
With forward declarations you can only declare members or parameters as pointer or reference to that type. You cannot use any methods or other things that require the innards of said type. That said I found forward declarations really limiting when trying to speed up compilation times. I suggest you investigate the possibility of precompiled headers a bit more since I found them to really help with compilation times, though that was with using Visual C++ on Windows and not g++.
使用前向声明,您只能将成员或参数声明为该类型的指针或引用。您不能使用任何需要所述类型的内脏的方法或其他东西。也就是说,我发现在尝试加快编译时间时,前向声明确实存在限制。我建议你多调查一下预编译头的可能性,因为我发现它们确实有助于缩短编译时间,尽管那是在 Windows 上使用 Visual C++ 而不是 g++。
回答by Eclipse
There is <iosfwd>
that will give you some forward declaration for the iostream classes, but in general there's not much you can do about the stl templates in terms of forward declaring them.
还有<iosfwd>
,会给你一些前瞻性声明iostream类,但总体上没有太多可以做的正向宣布这些条款的STL模板。
Pre-compiled headers are the way to go. You won't notice any speed-increase the first time you compile them, but you should only pay that price once for every time you modify the precompiled header (or anything included in it).
预编译的头文件是要走的路。第一次编译它们时您不会注意到任何速度提高,但是每次修改预编译头文件(或其中包含的任何内容)时,您应该只支付一次该费用。
See this questionfor other ideas about speeding up compilation.
有关加速编译的其他想法,请参阅此问题。