C++11类型擦除实现是否安全?是否存在未定义行为?
C++11实现的类std::any类型擦除类的安全性分析
问题描述
我基于C++11实现了一个类似std::any的类型擦除类,测试运行正常,但担心存在未定义行为(UB)。尤其关注Any::get()的实现,以及该类与多态类型的交互(子类地址可能与父类不一致)。请问该实现是否安全?
实现代码
#include <typeinfo> #include <memory> class Any { private: template <typename T> class TypeSelector {}; const std::type_info &m_type; std::unique_ptr<void, void(*)(const void *)&> m_value; template <typename T, typename... Args> Any(TypeSelector<T>, Args &&... args) : m_type( typeid(T) ), m_value( std::unique_ptr<void, void(*)(const void *)&> { new T ( std::forward<Args>(args)... ), [](const void *ptr) { const T *self = static_cast<const T *>(ptr); delete self; } } ) {} public: Any(Any &&) = default; template <typename T, typename... Args> static Any make(Args &&... args) { return Any(TypeSelector<T> {}, std::forward<Args>(args)...); } template <typename T> T *get() { if (m_type == typeid(T)) { return static_cast<T *>(m_value.get()); } else { return nullptr; } } };
使用示例
#include <string> #include <iostream> template <typename T> void print(const T *value) { if (value) { std::cout << *value << '\n'; } else { std::cout << "nullptr" << '\n'; } } int main() { Any str = Any::make<std::string>("Hello there"); Any num = Any::make<int>(54); print(str.get<std::string>()); // Hello there print(str.get<int>()); // nullptr print(num.get<std::string>()); // nullptr print(num.get<int>()); // 54 return 0; }
安全性分析
核心实现的安全性
Any::get()的类型转换
该方法仅在typeid(T)与存储对象的静态类型完全匹配时,才会将void*转换为T*。根据C++标准,当void*直接来源于T*的转换时,反向的static_cast<T*>是完全安全的,不存在未定义行为。
对于多态类型,由于typeid比较的是构造时指定的T的静态类型:如果存储子类对象但构造时指定父类类型(会触发对象切片),get<父类>会成功,get<子类>返回nullptr;如果构造时指定子类类型,get<父类>返回nullptr——这与std::any的行为一致,不会出现地址转换错误的风险。内存管理的正确性
存储对象的销毁由绑定到unique_ptr的lambda完成:lambda捕获了构造时的T类型,将const void*转换为const T*后执行delete。C++允许删除const限定的对象指针,且由于new创建的对象类型就是T,delete操作完全匹配,不存在类型不匹配导致的UB。
存在的功能缺失(非UB,但需注意)
- 不支持拷贝:类仅默认了移动构造函数,由于
unique_ptr不可拷贝,默认拷贝构造函数被删除,因此Any对象只能移动,无法拷贝。 - 缺少const版本的
get():当前get()只能在非const的Any对象上调用,无法操作const对象,不符合const正确性原则。 - 无错误处理机制:若用户尝试通过
get()获取不匹配类型的指针,仅返回nullptr,没有类似std::bad_any_cast的异常抛出机制(这是设计选择,而非UB)。
结论
该实现的核心逻辑是安全的,不存在未定义行为。在类型完全匹配的场景下可以可靠工作,多态类型交互时也不会出现地址转换错误的问题。但存在若干功能缺失,可根据需求补充完善。
内容的提问来源于stack exchange,提问作者Shahar Nacht
相关产品推荐
相关产品推荐

