news 2026/4/15 22:23:55

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/4/15 13:13:36

springboot大学社团管理系统

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

作者头像 李华
网站建设 2026/4/15 13:12:35

自考党必看!8个降AI率工具高效避坑指南

自考党必看&#xff01;8个降AI率工具高效避坑指南 AI降重工具&#xff1a;自考论文的高效避坑利器 在自考论文写作过程中&#xff0c;越来越多的学生开始关注“AIGC率”和“查重率”的问题。随着AI技术的广泛应用&#xff0c;许多学生在使用AI辅助写作时&#xff0c;发现论文中…

作者头像 李华
网站建设 2026/4/15 14:48:10

计算机毕业设计springboot“阴阳师”游戏玩家社区设计与实现 基于Spring Boot框架的“阴阳师”游戏爱好者社区开发与应用 Spring Boot驱动下的“阴阳师”游戏玩家互动社区构建与实

计算机毕业设计springboot“阴阳师”游戏玩家社区设计与实现d58pn &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。随着信息技术的飞速发展&#xff0c;互联网已经成为人们生活中…

作者头像 李华
网站建设 2026/4/15 3:40:38

YOLOFuse与FastStone Capture注册码、PyCharm激活码无关声明

YOLOFuse 多模态目标检测系统深度解析 在智能安防、夜间巡检和自动驾驶等实际场景中&#xff0c;单一可见光摄像头常常“力不从心”——低光照、雾霾遮挡、伪装目标等问题让传统目标检测模型频频失效。而红外图像凭借其对热辐射的敏感性&#xff0c;在黑暗或恶劣天气下依然能捕…

作者头像 李华
网站建设 2026/4/14 5:16:31

YOLOFuse与蔚来ET7集成:激光雷达+红外互补

YOLOFuse与蔚来ET7集成&#xff1a;激光雷达红外互补 在智能驾驶迈向L3及以上高阶阶段的今天&#xff0c;单一传感器已经难以应对全天候、全场景的感知挑战。夜间无光、浓雾弥漫、强逆光干扰——这些现实路况常常让可见光摄像头“失明”&#xff0c;而毫米波雷达又难以分辨静态…

作者头像 李华
网站建设 2026/4/9 3:15:21

AI重构招聘逻辑:HR的下一个十年,拼的是决策力

AI重构招聘逻辑&#xff1a;HR的下一个十年&#xff0c;拼的是决策力AI得贤招聘官当AI开始深度参与人才评估、甚至跻身招聘决策链&#xff0c;人力资源领域的游戏规则已悄然改写。一份全球调研显示&#xff0c;超75%的企业领导者不再将AI视为单纯工具&#xff0c;而是能并肩作战…

作者头像 李华