C++ std::any的模拟实现

std::any

std::any是C++标准库中的一个类,官网对它的描述如下:

类 any 描述用于任何可拷贝构造类型的单个值的类型安全容器。

  1. 类 any 的对象存储任何满足构造函数要求的类型的一个实例或为空,而这被称为 any 类对象的状态。存储的实例被称作所含对象。若两个状态均为空,或均为非空且其所含对象等价,则两个状态等价。
  2. 非成员 any_cast 函数提供对所含对象的类型安全访问。

换句话说,std::any对象可以存储任何类型的数据(单例等特殊情况除外)。这篇文章来探讨一下如何自己实现一个Any类。

Any的基本原理

在C++这种强类型语言中,想用一种类型来保存多种类型的数据,首先想到的就是用父类指针(或引用)来保存子类,实现运行时多态。但问题是,我们想要保存任意类型,必须使所有类型都有一个公共的父类。在某些语言(如Java)中,有一个Object类,是所有类的父类,因此这种语言中就非常容易实现。但C++的类型系统相当混乱,原生类型没有父类,STL的类型也没有一个公共父类,而自定义类型也不会自动继承自一个公共父类,因此直接用父类指针不可行。但是如果我们把模板和继承结合一下就可以了,为每一种类型创建一个对应的模板类,这个模板类又继承自一个父类。核心代码如下:

class AnyHelperBase
{
   
   
};
template<typename T>
class AnyHelper :public AnyHelperBase
{
   
   
	T data;
};

这样我们就可以用AnyHelperBase*类型来存储任意类型的数据了。当然,这只是大体思路,还需要具体完善。下面我们将以上述代码为母体,添加功能。

将数据存储到Any

Any类

在上面的代码中,如何将数据存储到Any?肯定需要一个AnyHelperBase*的类型。但考虑到直接操作指针不是很方便,并且std::any使用的时候并不需要指针,我们应该再写一个类来维护AnyHelperBase*

class Any
{
   
   
private:
	class AnyHelperBase
	{
   
   
	public:
	};
	template<typename T>
	class AnyHelper :public AnyHelperBase
	{
   
   
	public:
		T data;
	};
	AnyHelperBase* data;
public:
	
};

构造函数

接下来实现AnyHelper的构造函数(第一个是就地构造,直接通过参数构造data,后两个是拷贝构造):

template<typename ...Args>
AnyHelper(Args&&... args) :data(std::forward<Args>(args)...) {
   
   }
AnyHelper(const AnyHelper& other) :data(other.data) {
   
   }
AnyHelper(const T& value) :data(value) {
   
   }

Any类的构造函数:

Any() :data(nullptr) {
   
   }
template<typename T>
Any(const T& value) : data(new AnyHelper<std::decay_t<T>>(value)) {
   
   }
//Any(const Any& other) :data( ??? ) {}
Any(Any&& other) :data(other.data)
{
   
   
	other.data = nullptr;
}

注意:std::decay_t<T>的作用是去掉T的const,引用等乱七八糟的属性,比如std::decay_t<const int&>的结果是int。例如,我们显然不希望传入const intint得到不同的结果。这一点很重要,因为如果类型不匹配,后面获取数据时就会抛出异常!

拷贝构造的困难和解决方案

在写拷贝构造(上面代码的第三个函数)时,我们遇到了问题。由于是深拷贝,我们肯定不能直接复制指针,而是应该再new一个对象。但问题是,我们怎么获取另一个Any中的类型呢?这个问题似乎不好解决,因为只有在AnyHelper类内部我们才会知道存储的类型(这句话很重要)。但我们可以变通一下,让AnyHelper类直接返回一个自身的拷贝的指针,我们不必关心他具体是什么类型。当然,我们使用的是AnyHelperBase*,所以AnyHelperBase类里必须就得有这个函数,换句话说,这得是一个虚函数。在这里我们又用到了多态的特性。往AnyHelperBase和AnyHelper中添加Clone函数:

class AnyHelperBase
{
   
   
public:
	virtual AnyHelperBase* Clone()const = 0;
};
template<typename T>
class AnyHelper :public AnyHelperBase
{
   
   
public:
	T data;
	template<typename ...Args>
	AnyHelper(Args&&... args) :data(std::forward<Args>(args)...) {
   
   }
	AnyHelper(const AnyHelper& other) :data(other.data) {
   
   }
	AnyHelper(const T& value) :data(value) {
   
   }
	virtual AnyHelper* Clone()const
	{
   
   
		return new AnyHelper(*this);
	}
};

Any类的拷贝构造函数:

Any(const Any& other) :data(other.data->Clone()) {
   
   }

赋值运算符

赋值运算符和构造函数基本一样,需要注意的是delete原来的data

template<typename T>
Any& operator=(const T& value)
{
   
   
<think>嗯,用户这次提到了`blender::Any`,并且给出了具体的说明,包括它是类型安全的容器,类似`std::any`但有两个额外功能:可调整的内联缓冲区容量和对齐方式,以及存储用户自定义类型信息而不增加对象大小。首先,我需要确认这个`blender::Any`的来源和用途。 根据用户提供的上下文,这应该是指Blender的C++源代码中的一个内部类。之前用户提到过在Python API中没有这个模块,所以这次的信息可能来自Blender的开发者文档或源码注释。我需要解释清楚这个类的设计目的、功能特点,以及与标准库`std::any`的对比。 接下来要分析用户可能的背景。用户可能是有C++开发经验,正在参与Blender的贡献或者开发自定义模块,需要了解内部数据结构。用户的需求是理解何时使用`blender::Any`而不是`std::any`,以及它的优势在哪里。 需要详细说明两个新增功能:内联缓冲区和用户类型信息。内联缓冲区部分,要解释通常`std::any`使用小缓冲区优化,但大小不保证,而Blender的版本允许调整,这对于性能敏感的场景很重要,比如避免堆分配。用户自定义类型信息可能用于反射或其他需要在运行时获取类型信息的场景,而不会增加存储开销。 还要注意用户可能的应用场景,比如在Blender内部的数据处理、几何节点系统或自定义插件中。需要举例说明适合使用`blender::Any`的情况,比如需要高性能或自定义类型元数据时,否则使用`std::any`即可。 另外,要提醒用户这是C++ API的一部分,普通用户通过Python脚本不会直接接触,主要针对开发者。如果用户是在编写Python脚本,可能需要引导他们查阅其他相关文档或API。 最后,确保回答结构清晰,分点说明设计目标、功能对比、使用场景和注意事项,帮助用户全面理解`blender::Any`的用途和优势。</think>在 **Blender 的 C++ 源码**中,`blender::Any` 是一个为**高性能和灵活性**设计的类型安全容器,用于存储任意可复制构造(*copy-constructible*)类型的值。它类似于 C++17 标准库中的 `std::any`,但针对 Blender 的特定需求进行了优化扩展。以下是其核心特性解析: --- ### 一、设计目标 1. **类型安全** 与 `std::any` 类似,它能在运行时保存任意类型的值,同时通过类型检查避免非法类型操作。 2. **性能优化** 针对 Blender 高频操作场景(如几何节点、物理模拟)优化内存分配效率。 --- ### 二、与 `std::any` 的功能对比 | 特性 | `std::any` (C++17) | `blender::Any` (Blender) | |--------------------------|---------------------------|--------------------------------| | **内联缓冲区** | 通常有固定小缓冲区,但大小依赖实现 | 可自定义缓冲区的容量和对齐方式 | | **类型信息扩展** | 仅存储标准类型信息 | 支持附加用户自定义类型元数据 | | **内存分配策略** | 可能触发堆分配 | 通过调整内联缓冲区减少堆分配 | --- ### 三、关键扩展特性详解 #### 1. 可调内联缓冲区 (Adjustable Inline Buffer) - **原理** 通过模板参数或构造参数指定内联缓冲区的大小和对齐方式,例如: ```cpp blender::Any<64, 16> myValue; // 64字节内联缓冲区,16字节对齐 ``` - **优势** - 若存储的值体积 ≤ 内联缓冲区大小,直接在内联内存中存储,**避免堆分配**(提升性能)。 - 对于高频创建/销毁的小对象(如数学向量、矩阵),显著降低内存碎片。 #### 2. 用户自定义类型信息 - **原理** 允许通过类型特征(*type traits*)附加元数据(如类型名称、序列化方法),而不会增加 `Any` 对象本身的内存占用。 - **应用场景** - 运行时反射(如 GUI 中动态显示类型名称) - 自定义序列化/反序列化逻辑 - 跨模块传递类型时保留扩展信息 --- ### 四、使用场景建议 - **优先使用 `blender::Any` 的情况** - 需要严格控制内存分配(如实时交互操作) - 需附加类型元数据辅助处理逻辑 - 存储对象体积较大但可预估(通过内联缓冲区避免堆分配) - **可沿用 `std::any` 的情况** - 简单临时存储,无需高性能或扩展元数据 - 跨项目代码兼容性要求高 --- ### 五、注意事项 1. **仅限 C++ 开发** 此为 Blender 内部 C++ API,普通用户通过 Python API 操作 Blender 时无需直接接触。 2. **源码定位** 在 Blender 源码中可通过 `BLI_any.hh` 头文件找到实现,属于 `blender::` 命名空间下的底层工具类。 3. **替代方案参考** Blender 中类似容器还有 `blender::Variant`(类型受限但更高效)和 `blender::GPointer`(通用指针封装)。 若需进一步分析具体使用案例或性能对比,可提供代码片段或应用场景上下文。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值