具有内联所有虚函数的模板类

时间:2014-01-19 20:16:21

标签: c++ templates gdb inline member

我正在使用当前项目中包含虚函数的类模板,我偶然发现了一个我无法克服的问题。

  1. 类模板不能将其成员函数体从类中拆分 .hpp文件中的定义,因为链接器错误。我不想 为我即将使用的每种新类型实例化我的模板,所以全部 剩下的就是让它们内联。这绝对是 很好,因为他们大多数时间都是1-2行,所以我不会去 体验任何代码臃肿。
  2. 另一方面,gcc为多态类创建vtable .cpp文件,它定义了第一个非内联函数 在类定义中声明。因为我有所有成员 函数内联,我得到未定义的引用vtable,或者没有 在GDB中为我的班级找到了RTTI符号。
  3. 请考虑以下代码:

    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错误,我下次如何自行检查?

    1. 如果GDB抱怨缺少vtable - 会确认我可以通过引用使用派生类初始化的ABC来访问每个虚拟成员,这足以确认一切正常吗?
    2. 如果GDB抱怨丢失RTTI符号 - 在使用派生类初始化的ABC的引用上调用typeid()足以确认RTTI符号实际上是否存在?

1 个答案:

答案 0 :(得分:0)

您的代码(完整版,带有两个头文件和main.C)编译和链接没有任何错误,使用gcc 4.8.3,默认选项(-std = c ++ 11除外)启用C ++ 11模式)。

我甚至将生成的可执行文件加载到gdb中。 gdb吞没了它没有任何问题。

我在这里看不出任何错误。