C ++模板函数在头中编译但不是实现 [英] C++ template function compiles in header but not implementation
问题描述
我试图学习模板,我遇到了这个混淆的错误。我在一个头文件中声明一些函数,我想要一个单独的实现文件,其中的函数将被定义。这里是调用头(dum.cpp)的代码:
I'm trying to learn templates and I've run into this confounding error. I'm declaring some functions in a header file and I want to make a separate implementation file where the functions will be defined. Here's the code that calls the header (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):
Now, here's a working header file (dumper2.h):
#include <iostream>
#include <string>
#include <vector>
void test();
template <class T> void dumpVector( std::vector<T> v,std::string sep);
template <class T> void dumpVector(std::vector<T> v, std::string sep) {
typename std::vector<T>::iterator vi;
vi = v.begin();
std::cout << *vi;
vi++;
for (;vi<v.end();vi++) {
std::cout << sep << *vi ;
}
std::cout << "\n";
return;
}
执行(dumper2.cpp):
With implementation (dumper2.cpp):
#include <iostream>
#include "dumper2.h"
void test() {
std::cout << "!olleh dlrow\n";
}
奇怪的是,如果我移动定义dumpVector的代码。 h到.cpp文件,我得到以下错误。
The weird thing is that if I move the code that defines dumpVector from the .h to the .cpp file, I get the following error.
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
?
So why does it work one way and not the other? Clearly the compiler can find test()
, so why can't it find dumpVector
?
推荐答案
您遇到的问题是,编译器不知道要实例化的模板的哪些版本。当你将函数的实现移动到x.cpp时,它与main.cpp处于不同的转换单元,而main.cpp不能链接到特定的实例化,因为它在该上下文中不存在。这是C ++模板的一个众所周知的问题。有几个解决方案:
The problem you're having is that the compiler doesn't know which versions of your template to instantiate. When you move the implementation of your function to x.cpp it is in a different translation unit from main.cpp, and main.cpp can't link to a particular instantiation because it doesn't exist in that context. This is a well-known issue with C++ templates. There are a few solutions:
1)只需将定义直接放在.h文件中,就像以前一样。这有优点&包括解决问题(pro),可能使代码更不可读和不可读,
1) Just put the definitions directly in the .h file, as you were doing before. This has pros & cons, including solving the problem (pro), possibly making the code less readable & on some compilers harder to debug (con) and maybe increasing code bloat (con).
2)将实现放在x.cpp和 #includex.cpp
从 xh
内。如果这看起来很时髦和错误,只要记住 #include
只是读取指定的文件并编译它,就好像该文件是 x.cpp
换句话说,这确实是上面的解决方案#1,但它保持在单独的物理文件。当做这样的事情,关键是你不试图编译它自己的 #include
d文件。因此,我通常给这些类型的文件一个 hpp
扩展名,以区别于 h
文件和从 cpp
档案。
2) Put the implementation in x.cpp, and #include "x.cpp"
from within x.h
. If this seems funky and wrong, just keep in mind that #include
does nothing more than read the specified file and compile it as if that file were part of x.cpp
In other words, this does exactly what solution #1 does above, but it keeps them in seperate physical files. When doing this kind of thing, it is critical that you not try to compile the #include
d file on it's own. For this reason, I usually give these kinds of files an hpp
extension to distinguish them from h
files and from cpp
files.
#include <iostream>
#include <string>
#include <vector>
void test();
template <class T> void dumpVector( std::vector<T> v,std::string sep);
#include "dumper2.hpp"
文件:dumper2.hpp
File: dumper2.hpp
template <class T> void dumpVector(std::vector<T> v, std::string sep) {
typename std::vector<T>::iterator vi;
vi = v.begin();
std::cout << *vi;
vi++;
for (;vi<v.end();vi++) {
std::cout << sep << *vi ;
}
std::cout << "\n";
return;
}
3)由于问题是特定的实例化 dumpVector
对于尝试使用它的翻译单元是未知的,您可以在与定义模板的同一翻译单元中强制执行它。只需添加: template void dumpVector< int>(std :: vector< int> v,std :: string sep);
被定义为。这样,您不再需要中的
hpp
文件 #include
/ code>档案:
3) Since the problem is that a particular instantiation of dumpVector
is not known to the translation unit that is trying to use it, you can force a specific instantiation of it in the same translation unit as where the template is defined. Simply by adding this: template void dumpVector<int>(std::vector<int> v, std::string sep);
... to the file where the template is defined. Doing this, you no longer have to #include
the hpp
file from within the h
file:
#include <iostream>
#include <string>
#include <vector>
void test();
template <class T> void dumpVector( std::vector<T> v,std::string sep);
档案:dumper2.cpp
File: dumper2.cpp
template <class T> void dumpVector(std::vector<T> v, std::string sep) {
typename std::vector<T>::iterator vi;
vi = v.begin();
std::cout << *vi;
vi++;
for (;vi<v.end();vi++) {
std::cout << sep << *vi ;
}
std::cout << "\n";
return;
}
template void dumpVector<int>(std::vector<int> v, std::string sep);
顺便说一下,作为一个合计,你的模板函数取一个 vector
by-value 。你可能不想这样做,并通过引用或指针传递它,或者,更好的是,传递迭代器,以避免做一个临时&复制整个向量。
By the way, and as a total aside, your template function is taking a vector
by-value. You may not want to do this, and pass it by reference or pointer or, better yet, pass iterators instead to avoid making a temporary & copying the whole vector.
这篇关于C ++模板函数在头中编译但不是实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!