我正在尝试学习模板,但遇到了这个令人困惑的错误。我在头文件中声明了一些函数,并且我想创建一个单独的实现文件来定义函数。
这是调用标头的代码( dum.cpp
):
#include <iostream>
#include <vector>
#include <string>
#include "dumper2.h"
int main() {
std::vector<int> v;
for (int i=0; i<10; i++) {
v.push_back(i);
}
test();
std::string s = ", ";
dumpVector(v,s);
}
现在,这是一个工作头文件( dumper2.h
):
#include <iostream>
#include <string>
#include <vector>
void test();
template <class T> void dumpVector(const std::vector<T>& v,std::string sep);
template <class T> void dumpVector(const std::vector<T>& v, std::string sep) {
typename std::vector<T>::iterator vi;
vi = v.cbegin();
std::cout << *vi;
vi++;
for (;vi<v.cend();vi++) {
std::cout << sep << *vi ;
}
std::cout << "\n";
return;
}
通过实施( dumper2.cpp
):
#include <iostream>
#include "dumper2.h"
void test() {
std::cout << "!olleh dlrow\n";
}
奇怪的是,如果我将定义 dumpVector
的代码从 .h
移动到 .cpp
文件,我会收到以下错误:
g++ -c dumper2.cpp -Wall -Wno-deprecated
g++ dum.cpp -o dum dumper2.o -Wall -Wno-deprecated
/tmp/ccKD2e3G.o: In function `main':
dum.cpp:(.text+0xce): undefined reference to `void dumpVector<int>(std::vector<int, std::allocator<int> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
collect2: ld returned 1 exit status
make: *** [dum] Error 1
那么为什么它以一种方式工作而不是另一种呢?显然编译器可以找到 test()
,那为什么找不到 dumpVector
?
原文由 flies 发布,翻译遵循 CC BY-SA 4.0 许可协议
您遇到的问题是编译器不知道要实例化哪个版本的模板。当您将函数的实现移动到 x.cpp 时,它与 main.cpp 位于不同的翻译单元中,并且 main.cpp 无法链接到特定的实例化,因为它在该上下文中不存在。这是 C++ 模板的一个众所周知的问题。有几个解决方案:
只需将定义直接放入 .h 文件中,就像您之前所做的那样。这有利有弊,包括解决问题(pro),可能使代码的可读性降低以及在某些编译器上更难调试(con),并且可能会增加代码膨胀(con)。
将实现放入 x.cpp 和
#include "x.cpp"
从x.h
中。如果这看起来很奇怪和错误,请记住#include
只是读取指定的文件并编译它 ,就好像该文件是x.cpp
的一部分 正是上面的解决方案#1 所做的,但它将它们保存在单独的物理文件中。在执行此类操作时,请不要尝试自行编译#include
d 文件,这一点至关重要。出于这个原因,我通常给这些类型的文件一个hpp
扩展名,以将它们与h
文件和cpp
文件区分开来。文件:dumper2.h
文件:dumper2.hpp
dumpVector
的特定实例化,因此您可以在与模板所在的翻译单元相同的翻译单元中强制对其进行特定实例化定义。只需将以下内容添加:template void dumpVector<int>(std::vector<int> v, std::string sep);
… 到定义模板的文件中。这样做,您不再需要#include
hpp
文件来自h
文件:文件:dumper2.h
文件:dumper2.cpp
顺便说一句,总的来说,您的模板函数采用
vector
by-value 。您可能不想这样做,并通过引用或指针传递它,或者更好的是,传递迭代器以避免临时和复制整个向量。