您不能像这样转发声明类的“部分”。即使可以,您仍然需要在某处实例化代码,以便您可以链接它。有很多方法可以处理它,你可以让自己成为一个带有通用容器(例如向量)实例化的小库并将它们链接起来。然后你只需要编译例如矢量 一次。要实现这一点,您需要使用 -fno-implicit-templates 之类的东西,至少假设您坚持使用 g++ 并使用 template class std::vector<int> 显式实例化库中的模板
所以,一个真实的工作示例。这里我有 2 个文件,a.cpp 和 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:
g++ -fno-implicit-templates -c a.cpp
这会给我一个 a.o.如果我然后我尝试链接 a.o 我得到:
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:
#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<int>,这很好,但是其余的呢?简短的回答是,这些东西的实现必然有点混乱,当你手动实例化它们时,这些混乱的一部分会泄露出来。您可能想知道我是如何弄清楚我需要实例化什么的。好吧,我使用了链接器错误;)。
所以现在我们编译 b.cpp
g++ -fno-implicit-templates -c b.cpp
我们得到了 b.o.链接 a.o 和 b.o 我们可以得到
g++ a.o b.o
万岁,没有链接器错误。
因此,要详细了解您更新的问题,如果这是一门自酿课程,则不一定要如此混乱。例如,您可以将接口与实现分开,例如假设我们除了 a.cpp 和 b.cpp 之外还有 c.h、c.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.