std::any可存可复制或可移动的完整类型(如int、std::string、自定义struct),不能存引用、数组、抽象类、不完整类型、删除/私有拷贝构造的类型;取值须用std::any_cast避免异常。

std::any 能存什么类型,哪些不能存
std::any 可以存储任何可复制(copy-constructible)的完整类型,比如 int、std::string、自定义 struct(只要没删除拷贝构造函数)。但它不能存:引用类型(如 int&)、数组类型(如 int[5])、void、不完整类型(如前置声明但未定义的 class X;),也不能直接存 const 限定的对象(得靠 std::any_cast<const t></const> 取出)。
常见错误现象:std::any a = &x; 编译失败 —— 因为指针类型本身可存,但你写的是取地址表达式,类型是 int*,没问题;真正踩坑的是误以为能存“左值引用”,结果传了 std::ref(x) 却忘了 std::reference_wrapper 才是可复制的包装器。
- 想存引用语义?用
std::reference_wrapper<t></t>包一层,比如std::any a = std::ref(x); - 存函数对象?可以,
std::any a = [](int i){ return i*2; };合法(lambda 是可复制的闭包类型) - 存
std::unique_ptr?不行,它不可复制;改用std::shared_ptr
怎么安全地取出值:any_cast 的三种用法
取出值必须用 std::any_cast,否则编译不过。它不是类型转换函数,而是“带类型检查的解包”——运行时校验类型是否匹配,不匹配就抛 std::bad_any_cast。
三种常用形式:
- 值语义:
int i = std::any_cast<int>(a);</int>—— 要求a确实存的是int,否则抛异常 - 指针语义:
int* p = std::any_cast<int>(&a);</int>—— 安全,失败时返回nullptr,适合不确定类型时判断 - const 引用语义:
const std::string& s = std::any_cast<const std::string>(a);</const>—— 避免拷贝大对象,且能读 const 内容
注意:std::any_cast<t>(a)</t> 和 std::any_cast<t>(&a)</t> 参数类型不同,后者传的是 std::any*,别漏掉取地址符。
性能开销在哪:小对象优化和动态分配
std::any 实现通常有 SBO(small buffer optimization):小类型(如 int、std::size_t、短 std::string)直接存在内部缓冲区,不堆分配;大类型(如长字符串、大结构体)会 new 分配内存。这意味着:
- 存
int几乎零开销,和裸变量一样快 - 存
std::vector<double>(100000)</double>会触发堆分配 + 拷贝构造,有明显延迟 - 频繁存取大对象时,考虑是否真需要
std::any,还是用std::variant或指针/智能指针更合适
兼容性方面:C++17 起标准支持,GCC 7+、Clang 4+、MSVC 2017+ 都可用;老编译器需自行实现或换用 boost::any。
为什么赋值后原 any 变为空?移动语义陷阱
std::any 的移动赋值(a = std::move(b);)会让 b 变成空状态(b.has_value() == false),这是符合移动语义的预期行为,但容易被忽略。
典型问题场景:循环中反复移动一个 std::any 到容器里,第二次迭代时它已为空,导致 any_cast 抛异常。
- 避免方式:用拷贝赋值(
a = b;)代替移动,前提是类型支持拷贝 - 检查空值:取值前务必调用
a.has_value(),尤其在从容器或 API 返回的std::any上操作时 - 初始化习惯:声明时显式初始化,比如
std::any a = 42;,而不是默认构造后忘记赋值
最常被忽略的点是:把 std::any 当作“万能盒子”却忘了它本质是个带类型擦除的单值容器,没有“多态访问接口”,所有类型信息只在运行时靠 type() 查询,而查询结果只能用于调试或分支判断,不能直接用于泛型分发。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











