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

std::any 能存什么、不能存什么
std::any 只能持有可复制(CopyConstructible)或可移动(MoveConstructible)的类型,且必须是完整类型。空值(std::any{})是合法的,但不能直接用 std::any 存放引用、数组、抽象类、不完整类型(如前置声明的类)、或带有删除/私有拷贝构造函数的对象。
- 可以存:
int、std::string、std::vector<double></double>、自定义 struct(只要满足构造要求) - 不能存:
int&、void()(函数类型)、MyClass[](数组类型)、std::unique_ptr<int>&</int> - 常见误用:试图把
std::function<void></void>的返回值“直接塞进”std::any—— 实际上它本身可存,但若捕获了局部引用或 move-only 对象,运行时可能崩溃
如何安全地取出值:type() 和 has_value() 必须配合使用
直接调用 std::any_cast<t>(a)</t> 而不检查类型,会抛出 std::bad_any_cast。不能只靠 try-catch 代替类型判断——异常开销大,且掩盖逻辑缺陷。
- 先用
a.type() == typeid(T)判断类型是否匹配(注意:typeid 比较在跨编译单元时可能不可靠,优先用std::any_cast<t>(&a)</t>的空指针返回机制) - 更推荐写法:
if (auto p = std::any_cast<int>(&a)) { use(*p); }</int>—— 成功返回非空指针,失败返回nullptr,无异常、无拷贝 -
a.has_value()只说明非空,不代表能 cast 成任意类型;空std::any的type()返回typeid(void)
移动语义与性能陷阱:避免隐式拷贝和重复分配
std::any 内部通常采用小对象优化(SOO),对小类型(如 int、std::string_view)直接存栈上;大类型则堆分配。但无论大小,赋值/构造默认走拷贝——这在高频场景下很伤。
- 用
std::any{std::move(x)}显式移动,尤其对std::string、std::vector等类型能避免深拷贝 - 避免反复赋值:
a = std::string("hello"); a = std::string("world");会导致两次堆分配;改用a.emplace<:string>("world")</:string>可原地构造(C++17 起支持) -
std::any不提供swap成员函数,但可用std::swap(a, b)—— 它会自动触发高效移动交换
#include <any>
#include <string>
#include <iostream>
int main() {
std::any a = std::string("hello");
// ✅ 推荐:带空指针检查的取值
if (auto p = std::any_cast<std::string>(&a)) {
std::cout << *p << "\n"; // 输出 hello
}
// ❌ 危险:未检查就强转(抛异常)
// int x = std::any_cast<int>(a);
// ✅ 移动构造,避免拷贝
std::string s = "world";
a = std::any{std::move(s)}; // s 现在为空
// ✅ 原地构造新值
a.emplace<double>(3.14);
}
最常被忽略的是:即使你确定类型正确,也别绕过指针版 std::any_cast。它不抛异常、不拷贝、还能自然处理空值,比值版本更贴近真实工程需求。
立即学习“C++免费学习笔记(深入)”;










