深入解析 C++17 中的 std::any 实现

1,123 阅读3分钟

深入解析 C++17 中的 std::any 实现

在 C++17 标准中,std::any 提供了一种类型安全的方式来存储任意类型的值。它使用类型擦除(type erasure)技术实现,使得一个对象可以包含任何类型的值而不需要提前知道该类型。本文将详细解析 std::any 的内部实现原理。

一、类型擦除概念

类型擦除是指在运行时擦除类型信息,使得代码可以处理多种类型。std::any 使用类型擦除来存储任意类型的值,同时提供类型安全的访问方式。

二、std::any 的结构

std::any 的实现主要依赖于以下几个核心组件:

  1. any:主要的接口类,负责管理存储的值。
  2. any::placeholder:一个抽象基类,用于定义存储值的通用接口。
  3. any::holder:一个模板类,继承自 placeholder,用于具体存储某种类型的值。

1. any

any 类是用户直接使用的接口类。它包含一个指向 placeholder 基类的指针,用于管理存储的值。

class any {
public:
    any() noexcept : content(nullptr) {}

    template<typename ValueType>
    any(const ValueType& value)
        : content(new holder<std::decay_t<ValueType>>(value)) {}

    any(const any& other)
        : content(other.content ? other.content->clone() : nullptr) {}

    any(any&& other) noexcept : content(other.content) {
        other.content = nullptr;
    }

    ~any() { delete content; }

    any& operator=(const any& other) {
        if (this != &other) {
            delete content;
            content = other.content ? other.content->clone() : nullptr;
        }
        return *this;
    }

    any& operator=(any&& other) noexcept {
        if (this != &other) {
            delete content;
            content = other.content;
            other.content = nullptr;
        }
        return *this;
    }

    template<typename ValueType>
    any& operator=(const ValueType& value) {
        delete content;
        content = new holder<std::decay_t<ValueType>>(value);
        return *this;
    }

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

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

private:
    placeholder* content;
};

2. placeholder

placeholder 是一个抽象基类,定义了存储值的通用接口,包括获取类型信息和克隆自身的纯虚函数。

class placeholder {
public:
    virtual ~placeholder() = default;
    virtual const std::type_info& type() const noexcept = 0;
    virtual placeholder* clone() const = 0;
};

3. holder

holder 类是一个模板类,继承自 placeholder,用于存储具体类型的值。它实现了 placeholder 的虚函数,包括获取类型信息和克隆自身。

template<typename ValueType>
class holder : public placeholder {
public:
    holder(const ValueType& value) : held(value) {}
    holder(ValueType&& value) : held(std::move(value)) {}

    virtual const std::type_info& type() const noexcept override {
        return typeid(ValueType);
    }

    virtual placeholder* clone() const override {
        return new holder(held);
    }

    ValueType held;
};

三、std::any_cast 的实现

std::any_cast 是一个模板函数,用于从 any 对象中提取存储的值。它利用 dynamic_cast 来进行类型安全的转换。

1. 非指针版本

用于提取值的非指针版本:

template<typename ValueType>
ValueType any_cast(const any& operand) {
    auto p = any_cast<std::decay_t<ValueType>>(&operand);
    if (!p) throw bad_any_cast();
    return *p;
}

template<typename ValueType>
ValueType any_cast(any& operand) {
    auto p = any_cast<std::decay_t<ValueType>>(&operand);
    if (!p) throw bad_any_cast();
    return *p;
}

template<typename ValueType>
ValueType any_cast(any&& operand) {
    auto p = any_cast<std::decay_t<ValueType>>(&operand);
    if (!p) throw bad_any_cast();
    return std::move(*p);
}

2. 指针版本

用于提取值的指针版本:

template<typename ValueType>
const ValueType* any_cast(const any* operand) noexcept {
    if (operand && operand->type() == typeid(ValueType)) {
        return &static_cast<any::holder<ValueType>*>(operand->content)->held;
    }
    return nullptr;
}

template<typename ValueType>
ValueType* any_cast(any* operand) noexcept {
    if (operand && operand->type() == typeid(ValueType)) {
        return &static_cast<any::holder<ValueType>*>(operand->content)->held;
    }
    return nullptr;
}

四、总结

std::any 是 C++17 提供的一种灵活且类型安全的存储和传递任意类型值的工具。通过类型擦除技术,std::any 可以在运行时存储和管理不同类型的值。理解其实现原理有助于我们更好地使用这一工具,并在需要时进行适当的性能优化。