我想在模板类方法中使用迭代器。下面是我的代码:(测试等级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样式的迭代器。
5条答案
按热度按时间3j86kqsm1#
模板类方法必须在头文件中定义。当你使用模板类时,编译器实际上为给定的模板参数编译了该类的一个版本。因此,当包含头文件时,每个方法的主体都是可用的。
删除源文件并将主体包含在testclass.h中:
wr98u20j2#
模板类方法不需要在头文件中定义。但是如果你这样做,你需要定义一个单独的编译单元(例如templates.cpp),并且在其中你包括模板类的源代码文件(例如#include“container.cpp”//.cpp而不是.hpp文件),然后你需要定义你正在使用的模板的示例(例如模板类Container;)。您还需要为模板类定义对象(例如Link)。在这个特定的例子中,由于我们使用了指向这个对象的指针(例如在Containter中的Link*),我们只需要“向前声明”那个对象。
下面是完整的template.cpp文件,您可以编译它并将其与其余代码链接在一起。
我喜欢使用这种方法,因为它可以防止编译器自动生成模板类示例,并在无法找到时通知您。
使用选项-fno-implicit-templates编译gcc。
当您构建时,所有内容都将正常编译,但随后收集器将为使用该模板的所有对象重新编译templates.cpp文件。
f4t66c6m3#
如前所述,当模板示例化时,定义必须存在于同一编译单元中。
我个人倾向于将定义与声明分开。
这使得头文件更加简洁,并且在视觉上将接口与实现分开。
因此,一种解决方案可以如下:
您可以将实现和接口放在单独的文件中(即分别为
TestClass.hpp
和ITestClass.hpp
)。TestClass.hpp
最初将为#include ITestClass.hpp
,然后定义如上面示例中所示的函数。这样,客户端只需要
#include TestClass.hpp
。t3psigkw4#
也许值得看看这里的答案,看看
inline
是否适合您。https://stackoverflow.com/a/51585746/1440598pengsaosao5#
这在技术上是可行的,你只需要向链接器解释在哪里可以找到实现,但在真实的世界中,这很少做到,因为它需要分配大量的模板,并且不允许你在
.hpp
和.cpp
中使用另一个文件中的constexpr
变量,因为编译器会抱怨重定义的使用。我个人解决这个问题的一个方法是在另一个头文件中使用一个helper函数,然后从基本头文件中调用它。例如,假设我们在
encoder.hpp
中有一个类Encoder
,那么我们可以创建另一个名为encoder_impl.hpp
的文件,并执行以下操作。编码器_实现.hpp
编码器.hpp
这样做的缺点是,当使用普通的
.hpp
文件时,您的“实现”文件也会重新包含所有内容,但这并不会真正影响运行时性能,只会影响编译时间。