我正在使用Code :: Blocks来构建我的项目,其中包含三个文件:main.cpp
,TimeSeries.cpp
,TimeSeries.h
。 TimeSeries.h
提供TimeSeries
类的声明,如下所示:
template<class XType, class YType> class TimeSeries {
public:
TimeSeries(void);
~TimeSeries(void);
};
然后TimeSeries.cpp
包含:
#include“TimeSeries.h”
template<class XType, class YType>
TimeSeries<XType, YType>::TimeSeries(void) {
}
template<class XType, class YType>
TimeSeries<XType, YType>::~TimeSeries(void) {
}
最后,main.cpp
包含
#include "TimeSeries.h"
typedef TimeSeries<float, float> FTimeSeries;
int main(int argc, char** argv) {
FTimeSeries input_data;
return 0;
}
使用C :: B构建时,出现以下错误:
undefined reference to `TimeSeries<float, float>::TimeSeries()'
我该怎么办?
谢谢,
CFP。
答案 0 :(得分:3)
基本上所有模板化代码都应该在头文件中定义,否则不会构建它,因为在编译单元中没有使用它。
每个cpp文件都被编译为一个单独的单元,因此不会编译构造函数和析构函数。编译器无法知道在编译TimeSeries.cpp时将在main.cpp中使用哪种类型的模板参数。
答案 1 :(得分:2)
将代码拆分为头文件和源文件的原因是声明和实现是分开的。编译器可以将源文件(编译单元)转换为目标文件,而其他想要使用类和函数的编译单元只包括头文件,并链接目标文件。这样,代码只需编译一次,并可通过链接重用。
模板的问题在于,只要没有为它们提供参数,编译器就无法编译它们。使用不同参数实例化的相同模板会产生不同的类型。从编译器的角度来看,std::vector<int>
和std::vector<float>
与任何方式无关。因此,模板类通常必须完全驻留在头文件中,因为在使用模板时,编译器需要完整的定义才能根据参数生成类。
正如@Gabriel Schreiber在his answer中指出的那样,你可以告诉编译器他应该使用一组特定的参数编译模板,只需通过链接就可以将其提供给其他编译单元。但是,这不会使模板可用于其他参数集。
答案 2 :(得分:1)
您需要在.cpp文件中添加此内容(在定义下方):
template class TimeSeries<float, float>;
当编译器编译TimeSeries.cpp时,它不知道模板需要哪种类型,因为它在另一个源文件中使用。您需要明确地告诉编译器。
阅读您的Stroustrup副本或互联网上的显式模板实例化。