模板方法的未定义引用错误

rec*_*ion 43 c++ linker templates compilation

这让我疯狂了一个半小时.我知道这是一件小事,但找不到什么是错的(事实上,这是一个下雨的星期五下午,当然,没有帮助).

我已经定义了以下类,它将保存从文件中读取的配置参数,并允许我从我的程序中访问它们:

class VAConfig {
    friend std::ostream& operator<<( std::ostream& lhs, const VAConfig& rhs);

private:
    VAConfig();
    static std::string      configFilename;
    static VAConfig*        pConfigInstance;
    static TiXmlDocument*   pXmlDoc;
    std::map<std::string, std::string> valueHash;

public:
    static VAConfig* getInstance();
    static void setConfigFileName( std::string& filename ) { configFilename = filename; }
    virtual ~VAConfig();

    void readParameterSet( std::string parameterGroupName );
    template<typename T> T readParameter( const std::string parameterName );
    template<typename T> T convert( const std::string& value );
};
Run Code Online (Sandbox Code Playgroud)

其中方法convert()定义VAConfig.cpp

template <typename T>
T VAConfig::convert( const std::string& value )
{
    T t;
    std::istringstream iss( value, std::istringstream::in );
    iss >> t;
    return t;
}
Run Code Online (Sandbox Code Playgroud)

一切都很简单.但是当我从主程序中测试时使用

int y = parameters->convert<int>("5");
Run Code Online (Sandbox Code Playgroud)

我收到undefined reference to 'int VAConfig::convert<int>...'编译错误.同上readParameter().

看了很多模板教程,但不能弄清楚这一点.有任何想法吗?

Set*_*son 69

模板化代码实现永远不应该在.cpp文件中:您的编译器必须在看到调用它们的代码的同时看到它们(除非您使用显式实例化来生成模板化对象代码,但即使这样.cpp,文件类型也是错误的使用).

您需要做的是将实现移动到头文件,或者移动到诸如的文件VAConfig.t.hpp,然后#include "VAConfig.t.hpp"在使用任何模板化成员函数时.

  • 感谢 Seth 和 Dominic,我将实现移至头文件中并且它起作用了。我从未在我读过的任何教程中看到过这个方面。那么,为什么编译器需要在看到调用它们的代码的同时看到实现,即是什么使模板化函数在这方面独一无二? (3认同)
  • 有一些技术可以让模板远离,即前向声明模板特化。-1 表示“不应该” (3认同)
  • 编译器在实例化时需要提供整个模板定义 - 这样它就可以替换模板参数并对其进行评估.如果您的编译器支持它,您可以将模板声明为"extern",并以其他任何成员的方式使用它,但需要额外的链接时工作.GCC支持此作为扩展.它将成为C++ 0x标准的一部分. (2认同)

dma*_*dma 10

如果将模板化方法(convert和readParameter)的实现移动到头文件,它应该可以工作.

编译器必须能够在实例化它们的位置访问模板化函数的实现.


xto*_*ofl 5

模板方法仅仅是方法的...模板.模板参数将填入"实例化"方法的位置.

应该可以构建一个满足模板方法声明的编译器,并使用"模板编译"步骤编译模板方法所需的所有实例.

对于微软的vc来说,情况并非如此.不过,我听到一个同事咕mut着关于unix的情况.

大多数编译器根据请求实例化模板方法,在源代码中使用它们.为了实例化该方法,编译器必须"看到"模板函数体.这就是为什么主体通常放在头文件中,或者放在例如.h.cpp文件中,然后将其作为.h文件的最后一行包含在内.