std::any适用于运行时类型未知且不频繁变更的场景,如配置解析、插件传参、反射中间值;需用指针版std::any_cast安全取值,避免bad_any_cast异常,性能低于直接类型访问。

std::any 用在哪?不是万能容器
它只适合「运行时才知道类型,且类型确定后不频繁变更」的场景,比如配置解析、插件系统传参、反射中间值。别拿它当 std::vector<void></void> 用,更不该替代 std::variant 或模板泛型——类型擦除有开销,而且取值必须显式 std::any_cast,否则崩溃。
- 常见错误现象:
std::bad_any_cast异常,往往因为类型不匹配或对空std::any直接 cast - 典型使用场景:读取 JSON 配置项后暂存为
std::any,等具体模块加载后再按需转成int、std::string等 - 性能影响:构造/拷贝涉及堆分配(小对象优化可能避免,但不可依赖),访问比直接类型慢一个数量级
怎么安全地存和取?绕不开 std::any_cast
std::any 不提供自动类型转换,所有取值都得靠 std::any_cast,而且分引用版和指针版——后者才是防崩的关键。
- 正确做法:先用指针版尝试 cast,检查返回值是否非空,再解引用
if (auto p = std::any_cast<int>(&val)) { use(*p); }</int> - 错误做法:直接
std::any_cast<int>(val)</int>,一旦类型不对就抛std::bad_any_cast - 注意 const:存了 const 对象,cast 时也得用 const 类型,比如
std::any_cast<const std::string></const> - 移动语义支持:
std::any支持 move 构造和赋值,但 move 后原对象处于有效但未指定状态,别再读它
空值和类型查询:别假设它一定有东西
std::any 可以为空(默认构造),也能通过 has_value() 检查;type() 返回 std::type_info&,但不能直接比较类型名字符串——要用 == 比较 type_info 对象。
- 判断是否为空:
if (!val.has_value()) { /* 处理缺失 */ } - 安全比类型:
if (val.type() == typeid(double)) { ... },别写val.type().name() == "d"(平台相关,不可靠) - 类型不匹配时
std::any_cast<t>(&val)</t>返回nullptr,不是异常,这是你控制流程的机会 - 注意:
std::type_info::name()返回的字符串无标准格式,仅调试可用
和 std::variant 选哪个?看类型集合是否已知
如果运行时可能的类型只有几个(比如 int、double、std::string),优先用 std::variant:零开销、类型安全、支持 visit;std::any 是兜底方案,适用于类型完全开放(甚至来自动态库或脚本)的场合。
立即学习“C++免费学习笔记(深入)”;
-
std::variant编译期检查所有分支,std::any全靠运行时 assert -
std::any能存任意可复制类型,但无法遍历“可能的类型”——你得自己维护元信息 - 兼容性:两者都是 C++17,但
std::any在 MSVC 2017 和 GCC 7.1+ 才稳定,旧版本慎用 - 别混用:没有从
std::any自动推导std::variant的机制,转换要手动 dispatch
类型擦除本身不难,难的是在「灵活性」和「可控性」之间卡准那个点:多一层 if (auto p = std::any_cast<t>(&x))</t> 不是啰嗦,是唯一能防止程序在用户数据上当场哑火的方式。









