类型擦除对象的vtable链接错误原因及解决方法
类型擦除类编译链接错误分析与修复
问题背景
我正在为项目实现一个类型擦除类,简化实现代码如下:
#include <iostream> struct MyClass { struct TypeWrapper { virtual void print_value() const; }; template<typename ErasedType> struct ErasedTypeClass: public TypeWrapper { ErasedType object; ErasedTypeClass(ErasedType init_val): object(init_val) {} void print_value() const override { std::cout << object << '\n'; } }; TypeWrapper wrapper; }; int main() { MyClass class_object_int; class_object_int.wrapper = MyClass::ErasedTypeClass<int>(5); MyClass class_object_float; class_object_float.wrapper = MyClass::ErasedTypeClass<float>(10.0f); }
使用g++ -std=c++17编译时,编译环节无问题,但链接阶段抛出如下错误:
/cefs/ee/eed41a3e34266cd09176ffa5_gcc-trunk-20260515/bin/../lib/gcc/x86_64-linux-gnu/17.0.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/cclA5P5w.o: in function `main': <source>:27:(.text+0x9): undefined reference to `vtable for MyClass::TypeWrapper' /cefs/ee/eed41a3e34266cd09176ffa5_gcc-trunk-20260515/bin/../lib/gcc/x86_64-linux-gnu/17.0.0/../../../../x86_64-linux-gnu/bin/ld: <source>:30:(.text+0x36): undefined reference to `vtable for MyClass::TypeWrapper' /cefs/ee/eed41a3e34266cd09176ffa5_gcc-trunk-20260515/bin/../lib/gcc/x86_64-linux-gnu/17.0.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/cclA5P5w.o: in function `MyClass::TypeWrapper::TypeWrapper()': <source>:5:(.text._ZN7MyClass11TypeWrapperC2Ev[_ZN7MyClass11TypeWrapperC5Ev]+0x9): undefined reference to `vtable for MyClass::TypeWrapper' /cefs/ee/eed41a3e34266cd09176ffa5_gcc-trunk-20260515/bin/../lib/gcc/x86_64-linux-gnu/17.0.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/cclA5P5w.o:(.rodata._ZTIN7MyClass15ErasedTypeClassIfEE[_ZTIN7MyClass15ErasedTypeClassIfEE]+0x10): undefined reference to `typeinfo for MyClass::TypeWrapper' /cefs/ee/eed41a3e34266cd09176ffa5_gcc-trunk-20260515/bin/../lib/gcc/x86_64-linux-gnu/17.0.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/cclA5P5w.o:(.rodata._ZTIN7MyClass15ErasedTypeClassIiEE[_ZTIN7MyClass15ErasedTypeClassIiEE]+0x10): undefined reference to `typeinfo for MyClass::TypeWrapper' collect2: error: ld returned 1 exit status ASM generation compiler returned: 1 /cefs/ee/eed41a3e34266cd09176ffa5_gcc-trunk-20260515/bin/../lib/gcc/x86_64-linux-gnu/17.0.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/cc8zjuKh.o: in function `main': <source>:27:(.text+0x9): undefined reference to `vtable for MyClass::TypeWrapper' /cefs/ee/eed41a3e34266cd09176ffa5_gcc-trunk-20260515/bin/../lib/gcc/x86_64-linux-gnu/17.0.0/../../../../x86_64-linux-gnu/bin/ld: <source>:30:(.text+0x36): undefined reference to `vtable for MyClass::TypeWrapper' /cefs/ee/eed41a3e34266cd09176ffa5_gcc-trunk-20260515/bin/../lib/gcc/x86_64-linux-gnu/17.0.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/cc8zjuKh.o: in function `MyClass::TypeWrapper::TypeWrapper()': <source>:5:(.text._ZN7MyClass11TypeWrapperC2Ev[_ZN7MyClass11TypeWrapperC5Ev]+0x9): undefined reference to `vtable for MyClass::TypeWrapper' /cefs/ee/eed41a3e34266cd09176ffa5_gcc-trunk-20260515/bin/../lib/gcc/x86_64-linux-gnu/17.0.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/cc8zjuKh.o:(.rodata._ZTIN7MyClass15ErasedTypeClassIfEE[_ZTIN7MyClass15ErasedTypeClassIfEE]+0x10): undefined reference to `typeinfo for MyClass::TypeWrapper' /cefs/ee/eed41a3e34266cd09176ffa5_gcc-trunk-20260515/bin/../lib/gcc/x86_64-linux-gnu/17.0.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/cc8zjuKh.o:(.rodata._ZTIN7MyClass15ErasedTypeClassIiEE[_ZTIN7MyClass15ErasedTypeClassIiEE]+0x10): undefined reference to `typeinfo for MyClass::TypeWrapper' collect2: error: ld returned 1 exit status Execution build compiler returned: 1 Build failed
请问错误信息"undefined reference to 'vtable for MyClass::TypeWrapper'"具体含义是什么,该如何修复此问题?
错误含义解释
undefined reference to 'vtable for MyClass::TypeWrapper'表示链接器找不到TypeWrapper类的虚函数表(vtable)。在C++中,包含虚函数的类会由编译器生成虚函数表,用于实现动态多态机制。如果类中的非纯虚函数只有声明没有定义,编译器无法生成完整的虚函数表,链接阶段就会触发这个错误。
你的代码中TypeWrapper的print_value函数仅声明未定义,且未标记为纯虚函数,导致编译器无法生成该类的虚函数表。
修复方案
方案1:将print_value声明为纯虚函数
把TypeWrapper的虚函数改为纯虚函数,使其成为抽象基类,此时编译器不需要为它生成完整虚函数表,由派生类提供具体实现即可:
struct TypeWrapper { virtual void print_value() const = 0; // 纯虚函数,强制派生类实现 };
方案2:为print_value提供默认实现
若不想让TypeWrapper成为抽象基类,可以给该虚函数添加空的默认实现:
struct TypeWrapper { virtual void print_value() const {} // 空默认实现 };
额外问题:对象切片
修复链接错误后,代码仍存在对象切片问题:
class_object_int.wrapper = MyClass::ErasedTypeClass<int>(5);
派生类对象赋值给基类对象时,派生类的额外成员(如object)会被切割丢失,后续调用print_value会触发未定义行为。
解决此问题需使用指针或智能指针存储派生类对象,修改MyClass的成员定义如下:
#include <iostream> #include <memory> struct MyClass { struct TypeWrapper { virtual void print_value() const = 0; virtual ~TypeWrapper() = default; // 基类必须声明虚析构函数,避免内存泄漏 }; template<typename ErasedType> struct ErasedTypeClass: public TypeWrapper { ErasedType object; ErasedTypeClass(ErasedType init_val): object(init_val) {} void print_value() const override { std::cout << object << '\n'; } }; std::unique_ptr<TypeWrapper> wrapper; // 使用智能指针存储派生类对象 }; int main() { MyClass class_object_int; class_object_int.wrapper = std::make_unique<MyClass::ErasedTypeClass<int>>(5); class_object_int.wrapper->print_value(); // 输出5 MyClass class_object_float; class_object_float.wrapper = std::make_unique<MyClass::ErasedTypeClass<float>>(10.0f); class_object_float.wrapper->print_value(); // 输出10 }
这样就能完整保留派生类对象信息,实现类型擦除的预期效果。
内容的提问来源于stack exchange,提问作者Setu
相关产品推荐
相关产品推荐

