std::any仅支持可复制类型,禁用空值、函数指针、数组、delete拷贝构造类;存引用需std::reference_wrapper;取值前必须type_id()校验,无隐式转换;小对象栈内存储,大对象堆分配;不适用于数据持久化或序列化。

std::any 能存什么,不能存什么
它只接受可复制(CopyConstructible)类型,空值、函数指针、数组类型、带删除拷贝构造的类(比如 std::unique_ptr)直接编译报错——不是运行时失败,是模板实例化阶段就卡住。
常见踩坑:传 std::vector<int>&</int> 会静默转成右值引用绑定,实际存进去的是临时对象;想存引用必须用 std::reference_wrapper 包一层。
-
std::any a = std::string("hello");✅ -
std::any b = std::vector<int>{1,2,3};</int>✅ -
std::any c = std::ref(x);✅(x 是 int 变量) -
std::any d = std::unique_ptr<int>(new int(42));</int>❌ 编译失败
取值前必须先 type_id() 判断,否则抛 bad_any_cast
没有隐式转换,std::any_cast<t></t> 不会尝试类型推导或降级匹配。比如存了 long long,用 int 去取就是异常,哪怕值在范围内也不行。
典型错误现象:std::any_cast<int>(a)</int> 在 a 实际是 double 时崩溃,而不是返回 0 或截断。
立即学习“C++免费学习笔记(深入)”;
- 安全做法:先
a.type() == typeid(int)再 cast - 更稳写法:
auto p = std::any_cast<int>(&a); if (p) { use(*p); }</int>(返回指针,空指针表示失败) - 别依赖
std::any_cast<t></t>对 const 的宽容——它只对 const T& 和 T& 分别检查,不跨 cv 限定符自动适配
性能开销在哪?小对象 vs 大对象的内存布局差异
libstdc++ 和 libc++ 都实现了 small object optimization:内置约 32 字节缓冲区,小于等于这个尺寸的类型(如 int、std::string_view)直接存栈内,不 new;超过就堆分配。
这意味着频繁存 std::string(尤其短字符串)可能比存 std::shared_ptr<:string></:string> 还快;但存一个 1KB 的 struct 就必然触发堆分配 + 拷贝构造两次(入参一次,any 内部一次)。
- 避免反复赋值大对象:
a = big_struct;每次都可能 new + copy - 如果确定只读,考虑用
std::any const&接收,减少不必要的拷贝 - 调试时看
sizeof(std::any)—— 通常是 16 或 32 字节,和实现相关,但不代表能存多少
和 void* / union / std::variant 比较时该选谁
std::any 的唯一优势是“真任意”,但代价是运行时类型擦除 + 无编译期检查;std::variant 编译期穷举类型,访问快且安全,但必须提前列全;void* 完全裸奔,连类型信息都没留。
真实场景中,90% 的“万能容器”需求其实有隐含约束:比如配置项只可能是 int/double/std::string,这时候硬上 std::any 反而掩盖设计缺陷。
- 接口参数要泛型?优先用模板函数,不是 every type,是 each call site known type
- 配置解析结果?定义
using config_value = std::variant<int double std::string bool>;</int> - 插件系统传递上下文?
std::any合理,但建议加命名 key(如ctx.set("user_id", 123)),避免位置错乱
最常被忽略的一点:std::any 没有 operator==,也没办法序列化——它天生就不适合做数据载体,只适合做类型擦除的中间跳板。











