You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

安全性分析

核心实现的安全性

  1. Any::get()的类型转换
    该方法仅在typeid(T)与存储对象的静态类型完全匹配时,才会将void*转换为T*。根据C++标准,当void*直接来源于T*的转换时,反向的static_cast<T*>是完全安全的,不存在未定义行为。
    对于多态类型,由于typeid比较的是构造时指定的T的静态类型:如果存储子类对象但构造时指定父类类型(会触发对象切片),get<父类>会成功,get<子类>返回nullptr;如果构造时指定子类类型,get<父类>返回nullptr——这与std::any的行为一致,不会出现地址转换错误的风险。

  2. 内存管理的正确性
    存储对象的销毁由绑定到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 12:23:10