std::any 是仅存储单个可复制类型的运行时类型安全容器,取值必须用指针版 std::any_cast 检查,不支持 unique_ptr、数组、抽象类等,且无比较、哈希、序列化能力。

std::any 不是“万能容器”,它只存一个值,且取值前必须显式校验类型,否则 std::bad_any_cast 直接崩溃。
std::any 能存什么、不能存什么
它只接受可复制(CopyConstructible)类型:整数、std::string、std::vector<int></int>、自定义类(只要拷贝构造函数可用)都行;但以下情况编译失败:
-
std::unique_ptr<int></int>直接赋值会报错——它不可拷贝,得用std::any{std::move(ptr)}显式移动 - 空值(
nullptr_t)可以存,但std::any a;默认构造是空的,没存任何东西 - 函数指针、数组类型(如
int[5])、抽象类对象、不完整类型(如前置声明未定义的类)都不支持 - 析构函数不能抛异常,否则
std::any析构时可能调用std::terminate
字符串字面量陷阱:"hello" 存进去不是 std::string
写 data = "hello"; 时,实际存的是 const char*(C 风格指针),不是 std::string。这会导致后续 std::any_cast<:string>(data)</:string> 必然抛 std::bad_any_cast。
- 想存
std::string,必须显式构造:data = std::string("hello");或data = std::make_any<:string>("hello");</:string> - 读取时也得匹配:
if (auto p = std::any_cast<const char>(&data)) { use(*p); }</const> -
std::cout (data)能打印内容,是因为std::cout对const char*有特化,不是因为类型安全
怎么安全取出值:永远别裸用 std::any_cast<t>(a)</t>
裸调用会抛异常,而异常在配置解析、插件传参等高频路径里代价高,也难调试。正确姿势是用指针版 + 空指针检查:
立即学习“C++免费学习笔记(深入)”;
-
if (auto p = std::any_cast<int>(&a)) { use(*p); }</int>—— 类型对才进分支,无异常 - 存了
const对象(如const std::string s = "x"; a = s;),取时必须用std::any_cast<const std::string>(&a)</const>,否则类型不匹配 - 不确定类型又不想写一堆
if?说明你该换std::variant<int double std::string></int>,而不是硬扛std::any - 频繁访问时注意性能:每次
value()或any_cast都要运行时比对type_info,100 万次访问比std::variant慢 2–5 倍
小对象优化(SOO)和内存分配不可依赖
sizeof(std::any) 通常是 16 或 32 字节,但这只是控制块大小,不代表它能“免费”存多大对象。内部是否堆分配取决于实现和对象尺寸:
- 小类型(如
int、bool、短std::string)可能走 SOO,直接存在对象内 - 大对象(如长字符串、大
std::vector)大概率触发堆分配,且拷贝/移动开销明显 - 不要靠
sizeof(std::any)推断存储能力;也不要假设“存个std::vector<double>(1e6)</double>很便宜”——它真会 malloc - 若需高性能或确定类型集合,
std::variant几乎总是更优;std::any的唯一合理场景是插件系统、脚本绑定这类“真不知道将来塞啥”的动态边界
最容易被忽略的是:它没有 operator==,不支持哈希,也不能直接序列化——所有这些都得你手动加类型 tag 和逻辑。别把它当黑盒容器用,它是个带运行时成本的显式契约。


















