当前位置: 首页 > ops >正文

C++17 中std::any 详解和代码示例

1. 什么是 std::any

  • std::any 是 C++17 引入的一种类型安全的容器,可以存储任意类型的单个值。
  • 它类似于“类型擦除”的容器:存储时不关心具体类型,取出时需要显式指定类型。
  • void* 的区别:std::any 保留了类型信息,取出时能安全检查。

2. 定义与头文件

#include <any>std::any a;   // 默认空
  • 空状态:刚创建或 reset()any 为空 (has_value() == false)。

  • std::any 可以存放:

    • 内置类型(int, double, …)
    • 自定义类
    • 智能指针
    • 任何可拷贝构造的类型

3. 构造与赋值

std::any a = 10;          // 存 int
a = 3.14;                 // 存 double
a = std::string("Hello"); // 存 std::string
  • 拷贝构造:复制存储的对象
  • 移动构造:转移存储的对象,避免拷贝

4. 主要接口

① 存储

std::any a(42);          // 构造存储 int
a = std::string("Hi");   // 赋值新值

② 判断是否存储

if (a.has_value()) {std::cout << "Not empty\n";
}

③ 类型信息

std::cout << a.type().name() << std::endl; 

type().name() 返回的名字依赖编译器,不保证可读。

④ 取值

必须用 std::any_cast<T>

std::any a = 123;
int x = std::any_cast<int>(a); // OK

取引用,避免拷贝:

std::string s = "hello";
std::any a = s;std::string& ref = std::any_cast<std::string&>(a);  // 引用
ref[0] = 'H';  // 修改 a 内部的值

取指针,安全检查:

if (auto p = std::any_cast<int>(&a)) {std::cout << "Value = " << *p;
}

⑤ 清空

a.reset(); // 清空

5. 使用示例

#include <iostream>
#include <any>
#include <string>int main() {std::any a;a = 42;std::cout << std::any_cast<int>(a) << std::endl;a = std::string("Hello");std::cout << std::any_cast<std::string>(a) << std::endl;// 安全检查if (auto p = std::any_cast<std::string>(&a)) {std::cout << "string length = " << p->size() << std::endl;}
}

6. 实现原理(简要)

  • std::any 内部采用 类型擦除(type erasure) 技术:

    • 保存一个 void* 指针 + vtable(记录复制/析构函数)。
    • 存储时构建对象并保存类型信息 typeid(T)
    • 取出时 any_cast 检查类型是否匹配,不匹配则抛出 std::bad_any_cast 异常。

7. 常见场景

  • 通用容器(存放不同类型的数据):

    std::vector<std::any> vec;
    vec.push_back(1);
    vec.push_back(3.14);
    vec.push_back(std::string("hi"));
    
  • 插件系统 / 事件系统:不同模块通过 std::any 传递任意类型数据。

  • 配置参数存储:存储不同类型的配置项。


8. 常见坑点

  1. 取值类型必须完全匹配

    std::any a = 1; 
    std::cout << std::any_cast<long>(a); //  抛出异常
    

    必须用 int 取。

  2. 性能不是最好

    • std::any 内部需要堆分配(除非 SSO 优化)。
    • 如果追求性能,可以考虑 variant(固定类型集合)。
  3. 存储类型必须可拷贝

    • 不能存储 unique_ptr 这种不可拷贝对象(C++20 后可通过 make_any 支持移动构造)。

9. 对比其他类型擦除机制

特性std::anystd::variantvoid*
类型范围任意类型限定类型集合任意类型
类型安全
取值方式any_caststd::get强制转换
运行时开销较高较低较低

10.事件系统示例:std::any 消息总线

1. 基础实现

#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <functional>
#include <any>class EventBus {
public:using Handler = std::function<void(const std::any&)>;// 订阅事件void subscribe(const std::string& eventName, Handler handler) {handlers[eventName].push_back(handler);}// 发布事件void publish(const std::string& eventName, const std::any& data) {if (handlers.find(eventName) != handlers.end()) {for (auto& h : handlers[eventName]) {h(data);}}}private:std::unordered_map<std::string, std::vector<Handler>> handlers;
};

2. 使用示例

int main() {EventBus bus;// 订阅 int 事件bus.subscribe("score_update", [](const std::any& data) {if (auto p = std::any_cast<int>(&data)) {std::cout << "Score updated: " << *p << std::endl;}});// 订阅 string 事件bus.subscribe("message", [](const std::any& data) {if (auto p = std::any_cast<std::string>(&data)) {std::cout << "New message: " << *p << std::endl;}});// 订阅自定义类型事件struct Player {std::string name;int hp;};bus.subscribe("player_join", [](const std::any& data) {if (auto p = std::any_cast<Player>(&data)) {std::cout << "Player joined: " << p->name << ", HP=" << p->hp << std::endl;}});// 发布事件bus.publish("score_update", 100);bus.publish("message", std::string("Hello, EventBus!"));bus.publish("player_join", Player{"Alice", 150});return 0;
}

运行结果示例

Score updated: 100
New message: Hello, EventBus!
Player joined: Alice, HP=150

3.扩展示例

示例 1:广播多个监听者

bus.subscribe("message", [](const std::any& data) {if (auto p = std::any_cast<std::string>(&data)) {std::cout << "[Logger] " << *p << std::endl;}
});
bus.publish("message", std::string("Broadcast test"));

输出会触发 所有监听者

New message: Broadcast test
[Logger] Broadcast test

示例 2:事件驱动的游戏逻辑

bus.subscribe("enemy_killed", [](const std::any& data) {if (auto p = std::any_cast<int>(&data)) {std::cout << "Enemy killed, ID=" << *p << ", add 10 points!\n";}
});
bus.publish("enemy_killed", 42);

输出:

Enemy killed, ID=42, add 10 points!

示例 3:异构事件队列

std::vector<std::pair<std::string, std::any>> eventQueue;
eventQueue.push_back({"score_update", 200});
eventQueue.push_back({"message", std::string("Queued event")});
eventQueue.push_back({"player_join", Player{"Bob", 120}});// 批处理所有事件
for (auto& [eventName, data] : eventQueue) {bus.publish(eventName, data);
}

输出:

Score updated: 200
New message: Queued event
Player joined: Bob, HP=120

小总结

  • std::any 让消息总线可以存储任意类型事件数据
  • 取值时用 std::any_cast<T>,类型安全。
  • 适合事件系统、插件框架、异构数据流处理。

总结

  • std::any = 存任意类型的单值容器(运行时类型检查)。
  • 存取用 std::any_cast<T>,类型不匹配会抛异常。
  • 适合 异构容器、插件系统、配置存储
  • 缺点是 性能不如 variant,存取需小心类型。

http://www.xdnf.cn/news/18658.html

相关文章:

  • 【LeetCode 热题 100】416. 分割等和子集——(解法一)记忆化搜索
  • ansible的搭建与安装
  • 在数字化转型过程中,如何确保数据安全和隐私保护?
  • Linux 软件编程(十一)网络编程:TCP 机制与 HTTP 协议
  • 我的项目管理之路-组织级项目管理(二)
  • 【spring进阶】spring应用内方法调用时长统计
  • 【C语言强化训练16天】--从基础到进阶的蜕变之旅:Day13
  • Python之matplotlib 基础三:绘制折线图
  • 什么是JSON-RPC 2.0,在项目中应该怎么使用
  • Jenkins+docker 微服务实现自动化部署安装和部署过程
  • More Effective C++ 条款08:理解各种不同意义的new和delete
  • (操作系统)死锁是什么 必要条件 解决方式
  • 【Task05】:向量数据库实践(第三章3、4节)
  • Fory序列化与反序列化
  • ArcGIS JSAPI 高级教程 - 创建渐变色材质的自定义几何体
  • MYSQL(DDL)
  • 算法:驱动智能社会的核心引擎
  • OpenIM应用机器人自动应答
  • Zabbix 7.0中文乱码矫正
  • AI产品经理面试宝典第75天:Agentic RAG系统优化策略面试题实战解析
  • 08-系统能力调用与权限管理
  • BERT(Bidirectional Encoder Representations from Transformers)模型详解
  • 【RAGFlow代码详解-1】概述
  • 【Android】从一个AndroidRuntime看类的加载
  • 结构化智能编程:用树形向量存储重构AI代码理解范式
  • 第16届蓝桥杯C++中高级选拔赛(STEMA)2025年4月真题
  • More Effective C++ 条款05: 谨慎定义类型转换函数
  • 【Flex SerialPort】一个基于Qt6的支持自定义按键指令的串口工具
  • Kubernetes保姆级教学
  • centos搭建gitlab服务器