C++ std::any

一、基本概念

std::any 也是 C++17 引入的一个核心容器类。如果说 std::variant 是“类型安全的共用体(Union)”,那么 std::any 就是“类型安全的 void\* 指针”

它能够存储任意类型的单个值,并且在运行时保留该值的类型信息,确保在取出数据时进行强类型检查。

在 C++17 之前,如果你需要一个可以接收“任何类型”的容器,通常只能用 void*。但 void* 极其危险:

  1. 类型不安全:你可以把 int 的指针强转为 void*,然后再强转成 double* 取出来,编译器不会报错,但这会导致严重的内存损坏和未定义行为。
  2. 生命周期管理困难void* 不知道自己指向的对象何时该析构,容易导致内存泄漏。

std::any 完美解决了这些痛点:它既能存任何类型,又保证了类型安全和自动内存管理(RAII)

二、基本用法

声明、初始化与修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <any>
#include <string>
#include <vector>
#include <iostream>

int main() {
// 1. 默认初始化(为空)
std::any a;

// 2. 存储各种类型的值
a = 42; // 存储 int
a = 3.14159; // 存储 double
a = std::string("Hello std::any"); // 存储 std::string
a = std::vector<int>{1, 2, 3}; // 存储 vector

// 3. 使用 std::make_any 原位构造(推荐,避免临时拷贝)
std::any b = std::make_any<std::string>("Avoid extra copy");

// 4. 清空容器
b.reset(); // 现在 b 不包含任何值
}

类型检查与安全取出值,要获取 std::any 内部的值,必须使用 std::any_cast

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <any>
#include <string>
#include <iostream>

void printAnyValue(const std::any& a) {
if (!a.has_value()) {
std::cout << "Empty any\n";
return;
}

try {
// 如果 a 当前存的确实是 std::string
std::string s = std::any_cast<std::string>(a);
std::cout << "String: " << s << '\n';
} catch (const std::bad_any_cast& e) {
// 如果类型不匹配,会捕获到异常
std::cout << "Type mismatch: " << e.what() << '\n';
}
}

// 或

void safePrintAnyValue(const std::any& a) {
// 传入 &a (指针)
if (const int* pVal = std::any_cast<int>(&a)) {
std::cout << "Int: " << *pVal << '\n';
}
else if (const auto* pStr = std::any_cast<std::string>(&a)) {
std::cout << "String: " << *pStr << '\n';
}
else {
std::cout << "Other type or empty\n";
}
}

获取类型信息:

1
2
3
4
5
std::any a = 100;
if (a.type() == typeid(int)) {
std::cout << "Currently holding an int\n";
}
std::cout << "Type name: " << a.type().name() << '\n';

三、variant 与 any

这两者都是 C++17 的类型安全容器,但设计哲学截然不同。

维度 std::variant std::any
类型限定 闭集:必须在编译期列出所有可能类型(如 variant 开集:可存任何类型,无需预先声明
内存分配 完全在栈上,无动态内存开销 可能触发堆分配(大对象时)
类型检查 编译期配合 std::visit 可确保处理所有情况 运行期通过 any_cast 手动检查
访问效率 极高(接近原生类型) 较低(有 RTTI 和指针间接开销)
适用场景 状态机、多类型返回值、解析器结果 配置文件解析、通用事件派发、插件系统

四、注意

为了避免频繁的动态内存(堆)分配,绝大多数标准库实现对 std::any 使用了小对象优化

  • 如果存储的对象体积较小(通常等于或小于 2 到 3 个指针的大小,例如 intdoublestd::string_view、或者只有几个字段的小 struct),std::any 会将对象直接存储在栈上的内部缓冲区中。
  • 如果存储的对象体积较大(例如大 struct、较大的 std::array),std::any 会在堆上分配内存来存储该对象,并在内部保留一个指向堆内存的指针。

性能开销:

  1. 动态内存分配:对于大对象,每次赋值/拷贝都会触发 new / delete,开销较大。
  2. 运行时类型信息 (RTTI) 虚表开销std::any 内部包含一个函数指针或虚表指针,用于在运行时管理对象的拷贝、移动和析构。因此,调用 any_cast 和析构时会存在间接寻址开销。
  3. 不能内联:编译器很难对 std::any 的类型转换进行内联优化。

C++ std::any
http://example.com/2026/06/05/C++-std-any/
作者
Yu xin
发布于
2026年6月5日
许可协议