模板继承与纯虚类导致的‘类名未命名类型’问题
嘿,我一眼就看出你这问题的根源了——C++模板类的实现不能单独放在.cpp源文件里,这是模板的编译机制决定的,和前置声明、命名空间关系不大。
问题本质
模板不是现成的类型,它是用来生成具体类型的“蓝图”。当你把Thing的成员函数实现放到Thing.cpp里时,编译器编译这个.cpp文件时,只会处理模板的代码,但不会生成任何具体的Thing<int>或者Thing<double>这类实际类型的代码。等到其他文件(比如你的主程序)要使用Thing的时候,编译器只能看到头文件里的类声明,看不到.cpp里的实现,就会认为这个类的定义不完整,从而抛出“未命名类型”的错误。
你之前尝试移除Thing::限定符能编译,那是因为编译器把GetStatus()当成了全局函数,根本不是模板类的成员;移除命名空间也没用,因为核心矛盾是模板实例化的问题,和命名空间没关系。
两种修复方案
方案一:把模板实现移到头文件(推荐)
直接把Thing.cpp里的成员函数实现,放到Thing.hpp的类声明后面,这样编译器在处理任何使用Thing的文件时,都能看到完整的模板定义,就能正确生成具体类型的代码了:
修改后的Thing.hpp:
#ifdef _THING_H #define _THING_H #include "AbstractThing.hpp" namespace A { namespace B { template <class T> class Thing : public AbstractThing<T> { public: Thing(); ~Thing(); T GetStatus(); }; // 在这里添加成员函数的实现 template <class T> Thing<T>::Thing() {} template <class T> Thing<T>::~Thing() {} template <class T> T Thing<T>::GetStatus() { // 你的具体实现代码 } } } #endif
之后可以直接删除Thing.cpp文件。
方案二:显式实例化模板(仅适合固定类型的场景)
如果你一定要把实现留在.cpp文件里,那可以在Thing.cpp的末尾显式实例化你需要用到的所有模板类型,比如你只用到Thing<int>和Thing<std::string>,就加这两行:
#include "Thing.hpp" namespace A { namespace B { template <class T> Thing<T>::Thing() {} template <class T> Thing<T>::~Thing() {} template <class T> T Thing<T>::GetStatus() { ... } // 显式实例化需要的类型 template class Thing<int>; template class Thing<std::string>; } }
不过这个方案局限性很大,以后要是需要用到Thing<double>,必须手动添加新的显式实例化代码,所以一般只在特殊场景下用。
内容的提问来源于stack exchange,提问作者Max Feinberg

