c++ 对类模板成员函数的未定义引用

kcugc4gi  于 2023-03-05  发布在  其他
关注(0)|答案(5)|浏览(235)

我想在模板类方法中使用迭代器。下面是我的代码:(测试等级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);

我得到一个错误:

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样式的迭代器。

3j86kqsm

3j86kqsm1#

模板类方法必须在头文件中定义。当你使用模板类时,编译器实际上为给定的模板参数编译了该类的一个版本。因此,当包含头文件时,每个方法的主体都是可用的。
删除源文件并将主体包含在testclass.h中:

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

    }
};
wr98u20j

wr98u20j2#

模板类方法不需要在头文件中定义。但是如果你这样做,你需要定义一个单独的编译单元(例如templates.cpp),并且在其中你包括模板类的源代码文件(例如#include“container.cpp”//.cpp而不是.hpp文件),然后你需要定义你正在使用的模板的示例(例如模板类Container;)。您还需要为模板类定义对象(例如Link)。在这个特定的例子中,由于我们使用了指向这个对象的指针(例如在Containter中的Link*),我们只需要“向前声明”那个对象。
下面是完整的template.cpp文件,您可以编译它并将其与其余代码链接在一起。

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

我喜欢使用这种方法,因为它可以防止编译器自动生成模板类示例,并在无法找到时通知您。
使用选项-fno-implicit-templates编译gcc。
当您构建时,所有内容都将正常编译,但随后收集器将为使用该模板的所有对象重新编译templates.cpp文件。

f4t66c6m

f4t66c6m3#

如前所述,当模板示例化时,定义必须存在于同一编译单元中。
我个人倾向于将定义与声明分开。
这使得头文件更加简洁,并且在视觉上将接口与实现分开。
因此,一种解决方案可以如下:

//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

t3psigkw

t3psigkw4#

也许值得看看这里的答案,看看inline是否适合您。https://stackoverflow.com/a/51585746/1440598

pengsaosao

pengsaosao5#

这在技术上是可行的,你只需要向链接器解释在哪里可以找到实现,但在真实的世界中,这很少做到,因为它需要分配大量的模板,并且不允许你在.hpp.cpp中使用另一个文件中的constexpr变量,因为编译器会抱怨重定义的使用。
我个人解决这个问题的一个方法是在另一个头文件中使用一个helper函数,然后从基本头文件中调用它。例如,假设我们在encoder.hpp中有一个类Encoder,那么我们可以创建另一个名为encoder_impl.hpp的文件,并执行以下操作。

编码器_实现.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

编码器.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文件时,您的“实现”文件也会重新包含所有内容,但这并不会真正影响运行时性能,只会影响编译时间。

相关问题