“Undefined reference to” template class constructor
我不知道为什么会发生这种情况,因为我认为我已经正确地声明和定义了一切。
我有以下程序,是用模板设计的。它是队列的简单实现,具有成员函数"add"、"substract"和"print"。
我已经在精细的"nodo-colaypila.h"中定义了队列的节点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #ifndef NODO_COLAYPILA_H #define NODO_COLAYPILA_H #include <iostream> template <class T> class cola; template <class T> class nodo_colaypila { T elem; nodo_colaypila<T>* sig; friend class cola<T>; public: nodo_colaypila(T, nodo_colaypila<T>*); }; |
然后在"nodo-colaypila.cpp"中实现
1 2 3 4 5 6 7 8 | #include"nodo_colaypila.h" #include <iostream> template <class T> nodo_colaypila<T>::nodo_colaypila(T a, nodo_colaypila<T>* siguiente = NULL) { elem = a; sig = siguiente;//ctor } |
然后,队列模板类及其函数的定义和声明:
"可乐。H":
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #ifndef COLA_H #define COLA_H #include"nodo_colaypila.h" template <class T> class cola { nodo_colaypila<T>* ult, pri; public: cola<T>(); void anade(T&); T saca(); void print() const; virtual ~cola(); }; #endif // COLA_H |
"可乐·CPP":
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #include"cola.h" #include"nodo_colaypila.h" #include <iostream> using namespace std; template <class T> cola<T>::cola() { pri = NULL; ult = NULL;//ctor } template <class T> void cola<T>::anade(T& valor) { nodo_colaypila <T> * nuevo; if (ult) { nuevo = new nodo_colaypila<T> (valor); ult->sig = nuevo; ult = nuevo; } if (!pri) { pri = nuevo; } } template <class T> T cola<T>::saca() { nodo_colaypila <T> * aux; T valor; aux = pri; if (!aux) { return 0; } pri = aux->sig; valor = aux->elem; delete aux; if(!pri) { ult = NULL; } return valor; } template <class T> cola<T>::~cola() { while(pri) { saca(); }//dtor } template <class T> void cola<T>::print() const { nodo_colaypila <T> * aux; aux = pri; while(aux) { cout << aux->elem << endl; aux = aux->sig; } } |
然后,我有一个程序来测试这些功能,如下所示:
"主CPP"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #include <iostream> #include"cola.h" #include"nodo_colaypila.h" using namespace std; int main() { float a, b, c; string d, e, f; cola<float> flo; cola<string> str; a = 3.14; b = 2.71; c = 6.02; flo.anade(a); flo.anade(b); flo.anade(c); flo.print(); cout << endl; d ="John"; e ="Mark"; f ="Matthew"; str.anade(d); str.anade(e); str.anade(f); cout << endl; c = flo.saca(); cout <<"First In First Out Float:" << c << endl; cout << endl; f = str.saca(); cout <<"First In First Out String:" << f << endl; cout << endl; flo.print(); cout << endl; str.print(); cout <<"Hello world!" << endl; return 0; } |
但是当我构建模板类时,编译器会在模板类的每个实例中抛出错误:
未定义对"cola(float)::cola()"的引用。(实际上是可乐"<‘float’>"::cola(),但这不允许我这样使用。)
等等。总共有17个警告,其中包括程序中正在调用的成员函数的警告。
为什么会这样?定义了这些函数和构造函数。我认为编译器可以用"float"、"string"或其他东西替换模板中的"t";这是使用模板的优点。
我在这里的某个地方读到,出于某种原因,我应该将每个函数的声明放在头文件中。对吗?如果是这样,为什么?
事先谢谢。
这是C++程序设计中的一个常见问题。有两个有效的答案。两种答案都有优缺点,你的选择将取决于上下文。常见的答案是将所有的实现放在头文件中,但是在某些情况下,还有另一种方法是合适的。选择权属于你。好的。
模板中的代码只是编译器已知的"模式"。编译器不会编译构造函数
这个问题是由于
这两个答案是:好的。
- 告诉编译器,在
cola.cpp 的末尾,需要哪些特定的模板类,强制它编译cola 和cola 。 - 将成员函数的实现放在一个头文件中,该头文件将在每次任何其他"翻译单元"(如
main.cpp 使用模板类时包含该头文件)。
答1:显式实例化模板及其成员定义
在
1 2 | template class cola<float>; template class cola<string>; |
在
1 2 | template class nodo_colaypila<float>; template class nodo_colaypila<std :: string>; |
这将确保在编译器编译
在这种方法中,您应该确保所有实现都放在一个
常见的答案是将所有代码从实现文件
例如,STL和我们任何人将要编写的大多数代码中使用的默认答案是将所有实现放在头文件中。但是在一个更私有的项目中,您将拥有更多的知识和控制,可以实例化特定的模板类。事实上,这个"bug"可能被视为一个特性,因为它可以防止代码的用户意外地使用您没有测试或计划使用的实例化("我知道这适用于
最后,在您的问题中,代码中还有三个小的拼写错误:好的。
- 你在nodo-colaypila.h的末尾缺少一个
#endif 。 - 在cola.h中,
nodo_colaypila 应该是* ult, pri; nodo_colaypila ,两者都是指针。*ult, *pri; - nodo-colaypila.cpp:默认参数应该在头文件
nodo_colaypila.h 中,而不是在这个实现文件中。
好啊。
您必须定义头文件中的函数。不能在源文件中分离模板函数的定义,在头文件中分离声明。
当模板以触发其实例化的方式使用时,编译器需要查看该特定模板定义。这就是为什么模板经常在声明它们的头文件中定义的原因。
参考文献:C++03标准,〈7.7.2.4:
The definition of a non-exported function template, a non-exported member function template, or a non-exported member function or static data member of a class template shall be present in every translation unit in which it is explicitly instantiated.
编辑:澄清对评论的讨论:从技术上讲,有三种方法可以解决这个链接问题:
- 将定义移动到.h文件
- 在
.cpp 文件中添加显式实例化。 #include 使用模板在.cpp 文件中定义模板的.cpp 文件。
他们每个人都有各自的利弊,
将定义移动到头文件可能会增加代码大小(现代编译器可以避免这种情况),但肯定会增加编译时间。
使用显式实例化方法正在回到传统的类似宏的方法,另一个缺点是需要知道程序需要哪些模板类型。对于一个简单的程序来说,这很容易,但是对于复杂的程序来说,这很难提前确定。
虽然包含cpp文件同时令人困惑,但这两种方法都存在问题。
我发现第一个方法是最容易遵循和实现的,因此advocte使用它。
此链接解释了哪里出错:
[35.12]为什么我不能将模板类的定义从声明中分离出来,并将其放在.cpp文件中?
将构造函数、析构函数方法和其他内容的定义放在头文件中,这样可以纠正问题。
这提供了另一种解决方案:
如何避免模板函数出现链接器错误?
然而,这要求您预测模板将如何使用,并且作为一个通用的解决方案,这是违反直觉的。它确实解决了一个角点问题,尽管在这里,您开发了一个供某些内部机制使用的模板,并且您希望对模板的使用方式进行管理。