news 2026/6/10 3:34:36

C++万能类:any

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++万能类:any

std::any是 C++17 引入的一个极其重要的特性,它为 C++ 这种强类型语言带来了类似动态语言(如 Python 变量)的灵活性,同时保持了类型安全。

简单来说,std::any是一个类型安全的容器,它可以存储“任意”类型的单个值

以下是对std::any的详细讲解,分为用法实现原理、以及与其他技术的对比三个部分。


一、std::any的核心用法

在 C++17 之前,如果我们想在一个变量里存不同类型的数据,通常只能用void*(不安全,不仅丢失类型信息,还无法自动释放内存)或者union(极其局限)。std::any解决了这些问题。

1. 基础操作:存储与赋值

你可以将任何**可拷贝构造(Copy Constructible)**的类型赋值给std::any

#include <iostream> #include <any> #include <string> #include <vector> int main() { // 1. 默认构造(为空) std::any a; // 2. 存储 int a = 10; // 3. 存储 double (原本的 int 被销毁,类型变为 double) a = 3.14; // 4. 存储 std::string a = std::string("Hello World"); // 5. 存储复杂对象 a = std::vector<int>{1, 2, 3}; return 0; }
2. 访问数据:std::any_cast

这是std::any最关键的地方。由于std::any内部擦除了类型信息,编译器不知道里面存的是什么。取值时,你必须显式告诉它你要取什么类型。

  • 值/引用转换(抛出异常):如果类型不对,会抛出std::bad_any_cast
  • 指针转换(不抛异常):如果传入的是指针,类型不对时返回nullptr
#include <iostream> #include <any> int main() { std::any a = 100; try { // 【正确】类型匹配 int val = std::any_cast<int>(a); std::cout << "Value: " << val << std::endl; // 【错误】类型不匹配(虽然 100 是数字,但在 any 里它是 int,不是 float) // 这行会抛出 std::bad_any_cast float f = std::any_cast<float>(a); } catch(const std::bad_any_cast& e) { std::cout << "Error: " << e.what() << std::endl; } // 【安全访问模式】使用指针 // 如果 a 中存储的不是 int,这里 p 将是 nullptr,不会崩也不会抛异常 if (int* p = std::any_cast<int>(&a)) { std::cout << "Pointer access: " << *p << std::endl; } else { std::cout << "a does not contain an int" << std::endl; } }
3. 状态查询与重置
std::any a = 10; // 检查是否有值 if (a.has_value()) { // ... } // 获取类型信息 (type_info) if (a.type() == typeid(int)) { std::cout << "It's an integer!" << std::endl; } // 清空/重置 a.reset(); // 此时 has_value() 为 false

二、std::any的实现原理(深度解析)

很多同学会好奇:为什么 C++ 这种静态类型语言,能够在运行时随便换类型?

其核心技术被称为Type Erasure(类型擦除)

1. 核心架构:基类与模板子类

std::any的内部通常不直接存储值,而是持有一个指针,指向一个堆上(或栈上)的对象。为了能让这个指针指向任意类型,它利用了多态

我们可以尝试手写一个简化版的Any来理解:

class MyAny { private: // 1. 定义一个抽象基类(接口) struct StorageBase { virtual ~StorageBase() {} virtual std::unique_ptr<StorageBase> clone() const = 0; // 用于拷贝 any virtual const std::type_info& getType() const = 0; // 用于类型检查 }; // 2. 定义一个模板子类,用于存储具体的类型 T template<typename T> struct StorageImpl : StorageBase { T value; // 这里存具体的值 StorageImpl(T v) : value(v) {} // 实现虚函数 std::unique_ptr<StorageBase> clone() const override { return std::make_unique<StorageImpl<T>>(value); } const std::type_info& getType() const override { return typeid(T); } }; // 3. 成员变量:基类指针 std::unique_ptr<StorageBase> storage; public: // 构造函数:接受任意类型 template<typename T> MyAny(T v) : storage(std::make_unique<StorageImpl<T>>(v)) {} // ... 省略拷贝构造、赋值等 ... // 获取类型信息 const std::type_info& type() const { if (storage) return storage->getType(); return typeid(void); } // 友元函数用于 cast template<typename T> friend T* my_any_cast(MyAny* any); };

逻辑解析:

  1. 当我们执行MyAny a = 10;时,编译器推导出Tint
  2. 它实例化StorageImpl<int>,并将10存入其中的value
  3. MyAny内部持有StorageBase*指向这个StorageImpl<int>对象。
  4. 类型擦除:在MyAny这一层,它只知道自己持有StorageBase,不知道具体是int还是string。只有在运行时调用虚函数(如getType)或者强转回StorageImpl<int>时,才能恢复类型信息。
2. 性能优化:SBO (Small Buffer Optimization)

上述的简单实现有一个大问题:每次赋值都要new内存。如果我只存一个intbool,每次都在堆上分配内存,性能太差了。

工业级(STL)的实现通常引入了SBO(小缓冲优化)

  • 内部联合体std::any内部通常有一个union,包含一个void*指针(用于大对象)和一个小的字节数组(比如 16 字节或 32 字节)。
  • 判断大小
    • 如果存的对象很小(如int,double),直接存入内部字节数组,无需堆内存分配
    • 如果存的对象很大(如std::vector),才在堆上分配,并将指针存入。

这意味着,对于基础数据类型,std::any的性能是非常高效的。


三、 思考:std::anyvsvoid*vsstd::variant

为了更好地理解逻辑性问题,我们需要对比相似技术:

特性

std::any

std::variant (C++17)

void*

类型限制

无限制(只要能拷贝)

编译期确定的有限集合 (如int

ORstring

)

无限制

类型安全

安全(运行时检查,抛异常)

安全(编译期/运行时检查)

不安全(完全靠程序员自觉)

内存管理

自动 (RAII)

自动 (栈上分配)

手动 (容易内存泄漏)

存储位置

可能在堆,也可能在栈 (SBO)

只在栈上(大小等于最大成员的大小)

指向哪里就是哪里

性能

中等 (可能有虚函数/动态分配开销)

极高(无动态分配)

高 (裸指针)

使用场景

类型完全不可知,且开放

类型是已知的几种之一

与 C 语言接口交互

什么时候用std::any
  • 当你在设计一个通用的事件系统、消息总线、或者属性配置系统时。
  • 你不知道用户会传什么类型进来,可能是int,也可能是用户自定义的MyClass
  • 例子:Qt 的QVariant机制本质上就是std::any的变种,用于 GUI 控件存储任意用户数据。
什么时候用std::variant
  • 如果你的逻辑很明确:“这个变量要么是数字,要么是字符串,绝对不会是别的”。
  • 此时用std::variant<int, string>更好,因为它不需要动态分配内存,且编译器能帮你检查是否处理了所有类型。

四、 总结与建议

  1. std::any是现代 C++ 的“万能胶囊”:利用类型擦除技术,允许在这个容器里装入任何东西。
  2. 安全性:虽然它像动态类型,但它是类型安全的,必须通过any_cast显式还原类型,否则报错。
  3. 实现核心模板子类继承非模板基类+SBO 小对象优化
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 21:14:20

YOLOFuse是否收集用户数据?本地运行完全离线保障隐私

YOLOFuse是否收集用户数据&#xff1f;本地运行完全离线保障隐私 在安防监控、工业检测甚至自动驾驶等高敏感场景中&#xff0c;AI模型的“聪明”固然重要&#xff0c;但更关键的是——它是否值得信任。当一个目标检测系统接入摄像头时&#xff0c;我们不仅要问&#xff1a;它看…

作者头像 李华
网站建设 2026/6/9 21:17:21

springboot宠物分享网站

文章目录具体实现截图主要技术与实现手段系统设计与实现的思路系统设计方法java类核心代码部分展示结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;具体实现截图 本系统&#xff08;程序源码数据库调试部署讲解&#xff09;带文档1万…

作者头像 李华
网站建设 2026/6/10 2:03:39

导师严选10个AI论文写作软件,助你轻松搞定本科论文!

导师严选10个AI论文写作软件&#xff0c;助你轻松搞定本科论文&#xff01; AI 工具&#xff0c;让论文写作不再难 对于很多本科生来说&#xff0c;撰写一篇高质量的论文是大学生活中的一大挑战。从选题到大纲&#xff0c;再到初稿和修改&#xff0c;每一个环节都需要大量的时间…

作者头像 李华
网站建设 2026/6/9 22:25:49

Git远程协作

Git远程协作是借助远程库完成的 Git远程协作的过程 团队内协作过程 先提交原始版本团队所有经过授权的人都可以clone代码到自己本地库中进行修改修改完成后可以将该版本push到远程库中供创始人查看创始人可以pull别人push的代码加入自己的远程库中 团队间协作过程 先将别人团队…

作者头像 李华
网站建设 2026/6/9 21:20:33

YOLOFuse支持HuggingFace镜像加速下载?实测兼容性良好

YOLOFuse 支持 HuggingFace 镜像加速下载&#xff1f;实测兼容性良好 在夜间监控、边境安防或无人机巡检等实际场景中&#xff0c;传统基于RGB图像的目标检测系统常常“失明”——低光照、烟雾遮挡、逆光干扰让小目标几乎不可见。而红外&#xff08;IR&#xff09;成像虽能捕捉…

作者头像 李华