能否实现可获取基类指针的安全非UB版std::any?
问题
免责声明:本问题纯属假设,本人实际会直接使用抽象接口。
据我了解,std::any_cast<T>有一个重载版本,接受std::any指针,若其包含T则返回T指针,否则返回nullptr。
该行为与dynamic_cast类似,但局限性更大,因为dynamic_cast允许在继承链的任意类之间向上或向下转换。
但std::any_cast无法做到这点:必须先显式转换为包含的对象,再通过static_cast转换为基类,这限制了std::any的多态使用价值。
那么,是否能实现一个支持获取基类指针的安全非UB版any?
实现方案
可以实现一个支持多态转换的polymorphic_any,核心是在类型擦除时保留动态转换的能力,以下是一个安全无UB的实现:
#include <any> #include <typeinfo> #include <memory> #include <stdexcept> #include <type_traits> class polymorphic_any { private: struct placeholder_base { virtual ~placeholder_base() = default; virtual void* try_cast(const std::type_info& target) const = 0; virtual const std::type_info& type() const = 0; virtual placeholder_base* clone() const = 0; }; template<typename T> struct placeholder : placeholder_base { T value; explicit placeholder(T val) : value(std::move(val)) {} void* try_cast(const std::type_info& target) const override { // 优先匹配原始类型 if (typeid(T) == target) { return const_cast<void*>(static_cast<const void*>(&value)); } // 对多态类型尝试动态转换 if constexpr (std::is_polymorphic_v<T>) { return dynamic_cast<void*>(const_cast<T*>(&value)); } return nullptr; } const std::type_info& type() const override { return typeid(T); } placeholder_base* clone() const override { return new placeholder<T>(value); } }; std::unique_ptr<placeholder_base> content; public: polymorphic_any() = default; template<typename T, typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, polymorphic_any>>> polymorphic_any(T&& val) : content(std::make_unique<placeholder<std::decay_t<T>>>(std::forward<T>(val))) {} polymorphic_any(const polymorphic_any& other) { if (other.content) { content.reset(other.content->clone()); } } polymorphic_any(polymorphic_any&& other) noexcept = default; polymorphic_any& operator=(polymorphic_any&& other) noexcept = default; polymorphic_any& operator=(const polymorphic_any& other) { if (this != &other) { if (other.content) { content.reset(other.content->clone()); } else { content.reset(); } } return *this; } bool has_value() const noexcept { return content != nullptr; } const std::type_info& type() const noexcept { return content ? content->type() : typeid(void); } void reset() noexcept { content.reset(); } }; template<typename T> T* any_cast(polymorphic_any* any_ptr) { if (!any_ptr || !any_ptr->has_value()) { return nullptr; } void* ptr = any_ptr->content->try_cast(typeid(T)); return static_cast<T*>(ptr); } template<typename T> const T* any_cast(const polymorphic_any* any_ptr) { return any_cast<T>(const_cast<polymorphic_any*>(any_ptr)); } template<typename T> T any_cast(polymorphic_any& any_val) { T* ptr = any_cast<T>(&any_val); if (!ptr) { throw std::bad_any_cast(); } return *ptr; } template<typename T> T any_cast(const polymorphic_any& any_val) { return any_cast<const T>(const_cast<polymorphic_any&>(any_val)); }
关键说明
- 这个实现通过
placeholder<T>的try_cast方法,结合dynamic_cast实现了多态转换:当存储的类型T是多态类型(包含虚函数)时,能安全转换到其继承链中的基类或派生类指针,行为和dynamic_cast完全一致,保证无UB。 - 对于非多态类型,仅支持转换到原始类型,和标准
std::any行为一致,避免不安全的跨类型转换。 - 完整实现了值语义(拷贝、赋值),通过
clone方法完成存储对象的深拷贝,符合常规容器的使用习惯。
内容的提问来源于stack exchange,提问作者Sergey Kolesnik
相关产品推荐
相关产品推荐

