c++如何实现一个variant c++ std::any的简化版【实例】

简化版 variant 是轻量级 type-erased holder,支持有限类型集合的存储与安全取值;其核心由 void* 存储、type_info 类型标识和虚函数管理生命周期三要素构成,不追求完全兼容 std::variant 或 std::any。

什么是简化版 variant

我们不追求完全兼容 std::variantstd::any,而是实现一个轻量、可理解、支持有限类型集合的 type-erased holder,类似 std::any 的核心逻辑:能存任意给定类型(比如 intdoublestd::string),运行时知道当前存的是什么类型,并支持安全取值。

设计思路:用 void* + 类型 ID + 析构函数指针

关键三要素缺一不可:

  • 存储数据:用动态分配(或小对象优化,这里先用堆)+ void* 指向实际对象
  • 记录类型:用 std::type_info const* 或自定义类型 ID(推荐前者,直接且标准)
  • 管理生命周期:保存构造/拷贝/析构函数指针,确保对象被正确创建和销毁

代码实现(无依赖、C++11 可用)

下面是一个最小但完整可运行的简化版 my_any

#include 
#include 
#include 
#include 
#include 

class my_any { struct placeholder { virtual ~placeholder() = default; virtual const std::type_info& type() const = 0; virtual placeholder* clone() const = 0; virtual void destroy() = 0; };

templatezuojiankuohaophpcntypename Tyoujiankuohaophpcn
struct holder : placeholder {
    T value;
    holder(T&& v) : value(std::move(v)) {}
    holder(const T& v) : value(v) {}

    const std::type_info& type() const override { return typeid(T); }
    placeholder* clone() const override { return new holder(value); }
    void destroy() override { delete this; }
};

placeholder* ptr_ = nullptr;

public: my_any() = default;

templatezuojiankuohaophpcntypename Tyoujiankuohaophpcn
my_any(T&& v) : ptr_(new holder>(std::forward(v))) {}

my_any(const my_any& other) 
    : ptr_(other.ptr_ ? other.ptr_->clone() : nullptr) {}

my_any(my_any&& other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }

~my_any() { if (ptr_) ptr_->destroy(); }

my_any& operator=(const my_any& other) {
    if (this != &other) {
        if (ptr_) ptr_->destroy();
        ptr_ = other.ptr_ ? other.ptr_->clone() : nullptr;
    }
    return *this;
}

my_any& operator=(my_any&& other) noexcept {
    if (this != &other) {
        if (ptr_) ptr_->destroy();
        ptr_ = other.ptr_;
        other.ptr_ = nullptr;
    }
    return *this;
}

bool has_value() const { return ptr_ != nullptr; }

const std::type_info& type() const { 
    return ptr_ ? ptr_->type() : typeid(void); 
}

templatezuojiankuohaophpcntypename Tyoujiankuohaophpcn
T& get() & {
    if (!ptr_ || ptr_->type() != typeid(T))
        throw std::bad_cast();
    return static_castzuojiankuohaophpcnholderzuojiankuohaophpcnTyoujiankuohaophpcn*youjiankuohaophpcn(ptr_)->value;
}

templatezuojiankuohaophpcntypename Tyoujiankuohaophpcn
const T& get() const& {
    if (!ptr_ || ptr_->type() != typeid(T))
        throw std::bad_cast();
    return static_castzuojiankuohaophpcnholderzuojiankuohaophpcnTyoujiankuohaophpcn const*youjiankuohaophpcn(ptr_)->value;
}

};

使用示例

验证它能存、取、拷贝、移动不同类型:

int main() {
    my_any a = 42;
    std::cout << a.get() << "\n"; // 42
a = std::string("hello");
std::cout << a.getzuojiankuohaophpcnstd::stringyoujiankuohaophpcn() << "\n"; // hello

my_any b = a; // 拷贝构造
std::cout << b.getzuojiankuohaophpcnstd::stringyoujiankuohaophpcn() << "\n"; // hello

my_any c = std::move(a);
std::cout << c.getzuojiankuohaophpcnstd::stringyoujiankuohaophpcn() << "\n"; // hello
// a now empty — no crash on access
try {
    a.getzuojiankuohaophpcnstd::stringyoujiankuohaophpcn();
} catch (const std::bad_cast&) {
    std::cout << "a is empty or wrong type\n";
}

return 0;

}

关键点说明

这个简化版抓住了 std::any 的本质机制:

  • 所有类型擦除都靠 virtual 接口统一行为(clone/destroy/type
  • 模板 holder 在编译期为每种类型生成专属实现,避免运行时泛型开销
  • get运行时类型检查(typeid 对比),失败抛异常,不崩溃
  • 没有支持 std::any_cast 风格的指针版本,但加一层封装即可扩展

对比 std::any 的差异

原生 std::any 更健壮:支持小对象优化(SOO)、更严格的 SFINAE 约束、std::any_casthas_value() 返回 bool 而非空指针判断等。但本实现已覆盖最核心能力——类型擦除 + 安全访问 + RAII 管理,适合教学、嵌入式裁剪或理解原理。