对ClassName :: ClassName的未定义引用

时间:2010-09-19 07:15:11

标签: c++ class undefined-reference

我正在使用Code :: Blocks来构建我的项目,其中包含三个文件:main.cppTimeSeries.cppTimeSeries.hTimeSeries.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。

3 个答案:

答案 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副本或互联网上的显式模板实例化。