代码之家  ›  专栏  ›  技术社区  ›  Clément

对ClassName::ClassName的引用未定义

  •  0
  • Clément  · 技术社区  · 15 年前

    main.cpp , TimeSeries.cpp , TimeSeries.h 时间序列.h 提供的声明 TimeSeries 分类如下:

    template<class XType, class YType> class TimeSeries {
    public:
        TimeSeries(void);
        ~TimeSeries(void);
    };
    

    那么 时间序列.cpp #包括“TimeSeries.h”

    template<class XType, class YType>
    TimeSeries<XType, YType>::TimeSeries(void) {
    }
    
    template<class XType, class YType>
    TimeSeries<XType, YType>::~TimeSeries(void) {
    }
    

    最后, 主.cpp

    #include "TimeSeries.h"
    typedef TimeSeries<float, float> FTimeSeries;
    
    int main(int argc, char** argv) {
        FTimeSeries input_data;
        return 0;
    }
    

    undefined reference to `TimeSeries<float, float>::TimeSeries()'
    

    我能做什么?

    谢谢,

    3 回复  |  直到 15 年前
        1
  •  2
  •   Community CDub    8 年前

    将代码拆分为头文件和源文件的原因是,声明和实现是分开的。编译器可以将源文件(编译单元)转换为目标文件,而其他希望使用类和函数的编译单元只包含头文件,并链接目标文件。这样,代码只需编译一次,并且可以通过链接重用。

    模板的问题是,只要没有为模板提供参数,编译器就无法编译它们。用不同参数实例化同一个模板会导致不同的类型。 std::vector<int> std::vector<float> 从编译器的角度来看,它们之间没有任何关联。因此,模板类通常必须完全驻留在头文件中,因为在使用模板时,编译器需要完整的定义,以便根据参数生成类。

    正如@Gabriel Schreiber在 his answer ,您可以告诉编译器他应该使用一组特定的参数来编译模板,只需通过链接就可以让其他编译单元使用这些参数。但是,这不会使模板可用于其他参数集。

        2
  •  3
  •   Kleist    15 年前

    基本上,所有模板化的代码都应该在一个头中定义,否则它将不会被构建,因为编译单元中没有使用它。

    每个cpp文件被编译为一个单独的单元,因此不编译构造函数和析构函数。编译器无法知道您将在中使用哪种类型的模板参数主.cpp当它编译时时间序列.cpp.

        3
  •  1
  •   Gabriel Schreiber    15 年前

    template class TimeSeries<float, float>;
    

    编译器编译时时间序列.cpp它不知道需要哪个类型的模板,因为它在另一个源文件中使用。你需要明确地告诉编译器。

    阅读Stroustrup副本或互联网上的显式模板实例化。

    推荐文章