std::any可存储任何可复制构造类型(如int、std::string、自定义类等),但不能存储引用、数组、不完整类型或不可复制对象(如std::unique_ptr裸值);取值必须用std::any_cast显式转换,推荐优先使用指针版本避免异常。

std::any能存什么,不能存什么
std::any 可以持有任何可复制(CopyConstructible)类型的对象,包括自定义类、std::string、int、std::vector<double></double> 等。但它**不能持有引用、数组、不完整类型(如前置声明的类)、或不可复制的对象(如 std::unique_ptr 的裸值)**。
常见误用现象:std::any a{std::move(ptr)}; 看似在“移动”,但 std::any 构造时仍要求类型满足复制构造——它内部会尝试复制,而非转发移动语义。若类型无拷贝构造,编译直接失败。
实操建议:
- 想存独占资源(如
std::unique_ptr<t></t>),先包装成std::shared_ptr<t></t>再塞进std::any - 避免直接存 C 风格数组(如
int[5]),改用std::array<int></int>或std::vector<int></int> - 自定义类必须有公有拷贝构造函数;若禁用了拷贝(
= delete),std::any就无法容纳它
怎么安全地取回值:type() 和 std::any_cast 的配合
取出值不是“自动转型”,而是显式、带类型检查的强制转换。std::any 自身不提供隐式转换,所有读取都必须通过 std::any_cast,且失败时抛出 std::bad_any_cast 异常。
关键点:
-
a.type()返回std::type_info&,可用于运行时比对,但**不能直接用于模板推导**(比如不能写std::any_cast<a.type>(a)</a.type>) -
std::any_cast<t>(a)</t>要求T与存储类型完全一致(cv 限定符也要匹配),std::any_cast<const int>(a)</const>不能从存了int的std::any中成功取出 - 更安全的做法是先用
std::any_cast<t>(&a)</t>做指针尝试:返回nullptr表示类型不匹配,不抛异常
示例:
std::any a = 42;
if (auto p = std::any_cast<double>(&a)) {
// 不会进这里,因为存的是 int,不是 double
} else if (auto p = std::any_cast<int>(&a)) {
std::cout
<h3>性能开销在哪?什么时候不该用
</h3>
<p><code>std::any</code> 内部通常采用小对象优化(SOO):小类型(如 <code>int</code>、<code>bool</code>、短 <code>std::string</code>)直接存在对象内;大类型则堆分配。这意味着:</p>
<ul>
<li>存/取小类型很快,几乎无额外分配</li>
<li>存一个 <code>std::vector<char>(1024*1024)</char></code> 会触发一次 <code>new</code>,且后续拷贝构造也会深拷贝整个 vector</li>
<li>频繁 <code>std::any_cast</code> 指针检查(<code>std::any_cast<t></t></code>)比直接 cast 开销略高,但仍在纳秒级,一般无需担心</li>
</ul>
<p>不推荐场景:</p>
<ul>
<li>已知类型集合有限(如仅 <code>int</code>/<code>double</code>/<code>std::string</code>)→ 改用 <code>std::variant</code>,零运行时开销 + 编译期类型安全</li>
<li>高频存取同一类型 → 直接用原生类型或 <code>std::optional<t></t></code>,避免抽象层干扰</li>
<li>跨线程共享并频繁修改 → <code>std::any</code> 本身不保证线程安全,需外层加锁,此时抽象成本明显高于收益</li>
</ul>
<h3>如何避免 std::bad_any_cast 崩溃
</h3>
<p>崩溃往往发生在没做类型检查就直接 <code>std::any_cast<t>(a)</t></code>。最稳妥的模式是「先查后取」,而不是「捕获异常」。</p>
<p>理由:异常用于异常情况,而类型不匹配在使用 <code>std::any</code> 时很常见,属于预期中的控制流分支。</p>
<ul>
<li>永远优先用指针版 <code>std::any_cast<t>(&a)</t></code> 判断,非空再解引用</li>
<li>如果确实需要值语义(比如确定类型一定匹配),可用 <code>std::any_cast<t>(a)</t></code>,但仅限于你 100% 控制写入方的上下文(例如配置解析器内部,写入和读取逻辑紧耦合)</li>
<li>别依赖 <code>a.type() == typeid(T)</code> 做判断后调 <code>std::any_cast<t></t></code>:虽然多数实现下等价,但标准未保证 <code>type()</code> 的 <code>==</code> 行为完全等同于 <code>typeid</code> 比较,且多一次比较不如直接指针 cast 简洁</li>
</ul>
<p>类型擦除不是魔法,它把编译期约束移到了运行期——每次取值,你都得自己承担那个“是不是”的责任。</p></int></double>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











