我正在使用当前项目中包含虚函数的类模板,我偶然发现了一个我无法克服的问题.
>类模板不能将其成员函数体从类中拆分
.hpp文件中的定义,因为链接器错误.我不想
为我即将使用的每种新类型实例化我的模板,所以全部
剩下的就是让它们内联.这绝对是
很好,因为他们大多数时间都是1-2行,所以我不会去
体验任何代码臃肿.
>另一方面,gcc为多态类创建vtable
.cpp文件,它定义了第一个非内联函数
在类定义中声明.因为我有所有成员
函数内联,我得到未定义的引用vtable,或者没有
在GDB中为我的类找到的RTTI符号.
请考虑以下代码:
template <typename T> struct Test { virtual void testMe() const = 0; virtual ~Test() = default; }; template <typename T> struct test : public Test<T> { virtual void testMe() const { std::cout << typeid(T).name() << std::endl; } virtual ~test() = default; }; int main() { test<int> t; Test<int>& T = t; T.testMe(); return 0; }
在这个特殊的例子中,我得到:
can't find linker symbol for virtual table for `test<int>' value
用GDB调试时.
当所有类函数都是内联函数时,如何强制我的编译器将vtable放在特定的cpp文件中?
编辑:
由于上面提供的示例没有说明问题,这是我的原始代码.
导致问题的类:
#ifndef CONVERTIBLETO_H #define CONVERTIBLETO_H #include "convertibleTo_converters.h" #include <functional> template < typename IT,template <typename InterfaceType,typename ErasedType> class Converter = convertibleTo_detail::default_converter > class convertibleTo { public: typedef convertibleTo<IT,Converter> this_type; typedef IT InterfaceType; struct is_type_eraser_tag {}; private: class holder_interface { public: virtual InterfaceType get() const = 0; virtual void set(const InterfaceType&) = 0; virtual holder_interface* clone() const = 0; virtual ~holder_interface() {} }; template <typename ErasedType> class holder : public holder_interface { public: virtual InterfaceType get() const { return (Converter<InterfaceType,ErasedType>::convert(this->data)); } virtual void set(const InterfaceType& value) { this->data = (Converter<InterfaceType,ErasedType>::convert(value)); } virtual holder_interface* clone() const { return new holder(*this); } holder() = delete; holder(const holder& other): data(other.data) { } holder(ErasedType& d): data(d) { } virtual ~holder() = default; private: ErasedType& data; }; public: inline InterfaceType get() const { if (this->held) return this->held->get(); else return InterfaceType(); } inline void set(const InterfaceType& value) { if (this->held) this->held->set(value); } inline bool empty() const { return ! this->held; } convertibleTo<InterfaceType,Converter>& operator= (const convertibleTo<InterfaceType,Converter>& other) { if(this->held) delete this->held; this->held = other.held->clone(); return *this; } convertibleTo(): held(nullptr) { } template <typename T> explicit convertibleTo(T& data): held(new holder<T>(data)) { } convertibleTo( convertibleTo& other ): convertibleTo( const_cast<const convertibleTo&>(other)) { } convertibleTo( const convertibleTo& other ): held(nullptr) { if(other.held) this->held = other.held->clone(); } ~convertibleTo() { if (this->held) delete this->held; } private: holder_interface * held; }; #endif
必需的助手类:
#ifndef CONVERTIBLETO_CONVERTERS_H #define CONVERTIBLETO_CONVERTERS_H #include <string> #include <sstream> namespace convertibleTo_detail { template <typename InterfaceType,typename ErasedType> struct default_converter { static inline InterfaceType convert(const ErasedType& input) { return input; } static inline ErasedType convert(const InterfaceType& input) { return input; } }; template <typename T> struct default_converter<T,T> { static inline T convert(const T& input) { return input; } }; template <typename ErasedType> struct default_converter<std::string,ErasedType> { static inline std::string convert(const ErasedType& input) { default_converter<std::string,ErasedType>::prepareConverter(); default_converter<std::string,ErasedType>::converter << input; return default_converter<std::string,ErasedType>::converter.str(); } static inline ErasedType convert(const std::string& input) { default_converter<std::string,ErasedType>::prepareConverter(input); ErasedType result; default_converter<std::string,ErasedType>::converter >> result; return result; } private: static std::stringstream converter; struct SetExceptionFlagsOnce { SetExceptionFlagsOnce() { default_converter<std::string,ErasedType>::converter.exceptions(std::stringstream::failbit); } }; static void inline prepareConverter(std::string value = "") { static SetExceptionFlagsOnce setter; default_converter<std::string,ErasedType>::converter.clear(); default_converter<std::string,ErasedType>::converter.str(value); } }; template <typename ErasedType> std::stringstream default_converter<std::string,ErasedType>::converter; template <> struct default_converter<std::string,std::string> { static inline std::string convert(const std::string& input) { return input; } }; } #endif // CONVERTIBLETO_CONVERTERS_H
main.cpp中:
#include <iostream> #include "convertibleTo.h" int main() { int I = 5; convertibleTo< std::string > i(I); std::cout << i.get() << std::endl; i.set("321"); std::cout << i.get() << std::endl; return 0; }
我得到的错误是:
RTTI symbol not found for class 'convertibleTo<std::string,convertibleTo_detail::default_converter>::holder<int>'
当我进入i.get(),然后进入持有者的get()时,它会显示出来.
编辑:根据建议,从pastebin移动完整的源代码
由于最后两条评论表明这是一个GDB错误,我下次如何自行检查?
>如果GDB抱怨缺少vtable – 会确认我可以通过引用使用派生类初始化的ABC来访问每个虚拟成员,足以确认一切正常吗?
>如果GDB抱怨丢失RTTI符号 – 在使用派生类初始化的ABC的引用上调用typeid()足以确认RTTI符号实际上是否存在?