17370845950

c++中如何使用any类型_c++17 std::any用法与注意事项【详解】
std::any可存可复制或可移动的完整类型(如int、std::string),不能存引用、数组、抽象类、不完整类型或删除/私有拷贝构造的类型;取值应优先用指针版std::any_cast避免异常和拷贝。

std::any 能存什么、不能存什么

std::any 只能持有可复制(CopyConstructible)或可移动(MoveConstructible)的类型,且必须是完整类型。空值(std::any{})是合法的,但不能直接用 std::any 存放引用、数组、抽象类、不完整类型(如前置声明的类)、或带有删除/私有拷贝构造函数的对象。

  • 可以存:intstd::stringstd::vector、自定义 struct(只要满足构

    造要求)
  • 不能存:int&void()(函数类型)、MyClass[](数组类型)、std::unique_ptr&
  • 常见误用:试图把 std::function 的返回值“直接塞进” std::any —— 实际上它本身可存,但若捕获了局部引用或 move-only 对象,运行时可能崩溃

如何安全地取出值:type() 和 has_value() 必须配合使用

直接调用 std::any_cast(a) 而不检查类型,会抛出 std::bad_any_cast。不能只靠 try-catch 代替类型判断——异常开销大,且掩盖逻辑缺陷。

  • 先用 a.type() == typeid(T) 判断类型是否匹配(注意:typeid 比较在跨编译单元时可能不可靠,优先用 std::any_cast(&a) 的空指针返回机制)
  • 更推荐写法:if (auto p = std::any_cast(&a)) { use(*p); } —— 成功返回非空指针,失败返回 nullptr,无异常、无拷贝
  • a.has_value() 只说明非空,不代表能 cast 成任意类型;空 std::anytype() 返回 typeid(void)

移动语义与性能陷阱:避免隐式拷贝和重复分配

std::any 内部通常采用小对象优化(SOO),对小类型(如 intstd::string_view)直接存栈上;大类型则堆分配。但无论大小,赋值/构造默认走拷贝——这在高频场景下很伤。

  • std::any{std::move(x)} 显式移动,尤其对 std::stringstd::vector 等类型能避免深拷贝
  • 避免反复赋值:a = std::string("hello"); a = std::string("world"); 会导致两次堆分配;改用 a.emplace<:string>("world") 可原地构造(C++17 起支持)
  • std::any 不提供 swap 成员函数,但可用 std::swap(a, b) —— 它会自动触发高效移动交换
#include 
#include 
#include 

int main() {
    std::any a = std::string("hello");
    
    // ✅ 推荐:带空指针检查的取值
    if (auto p = std::any_cast(&a)) {
        std::cout << *p << "\n"; // 输出 hello
    }

    // ❌ 危险:未检查就强转(抛异常)
    // int x = std::any_cast(a);

    // ✅ 移动构造,避免拷贝
    std::string s = "world";
    a = std::any{std::move(s)}; // s 现在为空

    // ✅ 原地构造新值
    a.emplace(3.14);
}

最常被忽略的是:即使你确定类型正确,也别绕过指针版 std::any_cast。它不抛异常、不拷贝、还能自然处理空值,比值版本更贴近真实工程需求。