类模板成员函数未定义引用错误

10
我想在模板类方法中使用迭代器。 这是我的代码:(testclass.h)
template<typename T, typename container>
class TestClassX
{
public:
    void gen(typename container::iterator first );
};

而且文件 testclass.cpp:

template<typename T, typename container>
void TestClassX<T, container>::gen(typename container::iterator first)
{

}

当我尝试运行它时:

TestClassX<unsigned, std::vector<unsigned> > testx;
testx.gen(it);

I get an error:

Error:undefined reference to `TestClassX<unsigned int, std::vector<unsigned int, std::allocator<unsigned int> > >::gen(__gnu_cxx::__normal_iterator<unsigned int*, std::vector<unsigned int, std::allocator<unsigned int> > >)'

我使用mingw32 4.4。

我想要一个类,可以写入不同的容器,如std::vector、std::list、QVector或QList,所有这些容器都具有STL风格的迭代器。


可能是[Undefined reference to template members]的重复问题 (https://dev59.com/_lHTa4cB1Zd3GeqPTaQh)。 - kennytm
5个回答

13

模板类方法必须在头文件中定义。当您使用模板类时,编译器实际上会为给定的模板参数编译该类的版本。因此,在包含头文件时,每个方法的主体都必须可用。

删除源文件并将其包含在testclass.h中:

template<typename T, typename container>
class TestClassX
{
public:
    void gen(typename container::iterator first ) {

    }
};

模板类方法不需要在头文件中定义。你只需要让链接器找到它即可。同意 @Thaddeus 的观点。 - eric-haibin-lin

4

模板类方法不需要在头文件中定义。但如果你想这样做,你需要定义一个单独的编译单元(例如templates.cpp),在其中包含模板类的源代码文件(例如#include“container.cpp” // .cpp而非.hpp文件)然后您需要定义您正在使用的模板的实例(例如模板类Container;)。您还需要定义模板类的对象(例如Link)。在这种特殊情况下,由于我们使用指向该对象的指针(例如Link*,在Containter中),因此我们只需要“前向声明”该对象。

这是完整的template.cpp文件。您需要与其余的代码一起编译和链接。

class Link;
#include "Container.cpp"    // use the source code, not the header
template class Container<Link*>; 

我喜欢使用这种方法,因为它可以防止编译器自动化生成模板类实例,并在找不到时提示你。

使用gcc编译器并加上选项-fno-implicit-templates进行编译。

当你构建代码时,所有内容都会正常编译,但是收集器将重新编译使用模板的所有对象的templates.cpp文件。


0

如先前所述,在模板实例化时,定义必须存在于同一编译单元中。

个人更喜欢将定义与声明分开。
这样可以使头文件更加清晰,并在视觉上区分接口和实现。

因此,一种解决方案可以如下:

//TestClass.hpp

//Interface:
template<typename T>
class TestClassX
{
public:
    void gen(int a);
    //more declaraions...
}; 

//Implementations:
template<typename T>
void TestClassX<T>::gen(int a)
{
    //beautiful code
}

你可以将实现和接口放在不同的文件中(即分别为TestClass.hppITestClass.hpp)。 TestClass.hpp最初将#include ITestClass.hpp,然后按照上面的示例定义函数。
客户端只需要#include TestClass.hpp即可。

0

0

这是技术上可行的,你只需要向链接器解释实现可以在哪里找到,但在实际经验中,这很少被做到,因为它需要大量的样板文件,并且不允许你在.hpp.cpp文件中使用另一个文件中的constexpr变量,因为编译器会抱怨重定义的使用。

我个人解决这个问题的方法之一是在另一个头文件中使用帮助函数,并从基本头文件调用它。例如,假设我们有一个名为Encoder的类在encoder.hpp中,那么我们可以创建另一个名为encoder_impl.hpp的文件并执行以下操作。

encoder_impl.hpp

#include ... // include what you need. 
enum class ENCODE_TYPE : u8 { ... };
enum class CONN_TYPE : u8 { ... };

namespace encoder_impl {

 template<ENCODE_TYPE E, CONN_TYPE C>
 static inline void helper(...){...};

 template<> void helper<ENCODE_TYPE::LOGON, CONN_TYPE::QUOTE>
 (...){...}; 

} // encoder_impl

encoder.hpp

#include ... // include what you need. 
#include "encoder_impl.hpp"

namespace encoder {

template <CONN_TYPE C>
struct Encoder{

  template<ENCODE_TYPE E>
  inline void encode(...){
    encoder_impl::helper<E, C>(...);
  }

  inline buffer_conn<C>& get_buff() const { return buff; };

private:
  buffer_conn<C> buff; //just an example needing CONN_TYPE

} // encoder

这样做的缺点是,当使用普通的.hpp文件时,您的“实现”文件也会重新包含所有内容,但这并不会真正影响运行时性能,只会影响编译时间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接