std::any是C++17标准库中提供的类型擦除容器,能够存储任意满足可拷贝构造条件的类型数据,并且在使用时可以通过类型检查确保取值安全,避免了传统void*指针带来的类型风险。

std::any的基本特性
std::any的核心能力是类型擦除,它会自动管理存储数据的生命周期,当std::any对象销毁时,内部存储的数据也会被自动释放。它支持存储基础类型、自定义类型、标准库容器等各类可拷贝构造的类型,但是不支持存储数组类型,也不支持存储引用类型。
存储数据到std::any
向std::any中存入数据非常简单,可以直接通过赋值操作或者构造函数完成,以下是基础示例:
#include <any>
#include <iostream>
#include <string>
int main() {
// 存储int类型
std::any a1 = 10;
// 存储double类型
std::any a2 = 3.14;
// 存储字符串类型
std::any a3 = std::string("hello std::any");
// 存储自定义类型
struct Person {
std::string name;
int age;
};
std::any a4 = Person{"张三", 20};
return 0;
}
判断std::any中存储的类型
在从std::any中取出数据之前,需要先判断内部存储的类型是否符合预期,避免类型不匹配导致的异常。可以使用type()方法获取存储类型的type_info,再和目标类型对比:
#include <any>
#include <iostream>
#include <string>
#include <typeinfo>
int main() {
std::any a = 100;
// 判断存储的是否为int类型
if (a.type() == typeid(int)) {
std::cout << "存储的类型是int" << std::endl;
}
// 判断存储的是否为string类型
if (a.type() == typeid(std::string)) {
std::cout << "存储的类型是string" << std::endl;
}
return 0;
}
另外也可以使用has_value()方法判断std::any对象是否存储了有效数据:
#include <any>
#include <iostream>
int main() {
std::any a;
// 初始状态没有存储数据
if (!a.has_value()) {
std::cout << "当前std::any对象没有存储数据" << std::endl;
}
a = 50;
if (a.has_value()) {
std::cout << "当前std::any对象已经存储了数据" << std::endl;
}
return 0;
}
从std::any中取出数据
取出std::any中的数据有两种常用方式,分别是std::any_cast和std::any_cast配合指针使用,两种方式在类型不匹配时的行为不同。
使用std::any_cast直接取值
如果类型匹配,会返回对应的值,如果类型不匹配,会抛出std::bad_any_cast异常:
#include <any>
#include <iostream>
#include <string>
int main() {
std::any a = 200;
try {
// 正确取出int类型
int val = std::any_cast<int>(a);
std::cout << "取出的值: " << val << std::endl;
// 错误尝试取出string类型,会抛异常
std::string s = std::any_cast<std::string>(a);
} catch (const std::bad_any_cast& e) {
std::cout << "类型转换失败: " << e.what() << std::endl;
}
return 0;
}
使用std::any_cast取指针
这种方式不会抛出异常,如果类型匹配返回对应类型的指针,不匹配则返回空指针:
#include <any>
#include <iostream>
#include <string>
int main() {
std::any a = std::string("test");
// 取string类型指针
std::string* sp = std::any_cast<std::string>(&a);
if (sp != nullptr) {
std::cout << "取出字符串: " << *sp << std::endl;
}
// 取int类型指针,返回空
int* ip = std::any_cast<int>(&a);
if (ip == nullptr) {
std::cout << "类型不匹配,返回空指针" << std::endl;
}
return 0;
}
清空std::any中的数据
如果需要清空std::any中存储的数据,可以调用reset()方法,或者给它赋一个空的std::any对象:
#include <any>
#include <iostream>
int main() {
std::any a = 100;
std::cout << "清空前是否有值: " << a.has_value() << std::endl;
// 方式1:调用reset
a.reset();
std::cout << "reset后是否有值: " << a.has_value() << std::endl;
// 方式2:赋空值
a = 200;
a = std::any();
std::cout << "赋空后是否有值: " << a.has_value() << std::endl;
return 0;
}
使用注意事项
- std::any只能存储可拷贝构造的类型,如果存储的类型没有拷贝构造函数,会导致编译错误。
- 频繁使用std::any会带来一定的性能开销,因为类型擦除需要额外的内存和管理逻辑,对性能要求极高的场景需要谨慎使用。
- 取出的数据如果是大型对象,建议使用引用或者指针的方式,避免不必要的拷贝。
典型应用场景
std::any适合用在需要存储多种不确定类型的场景,比如通用配置项存储,配置项可能是int、string、bool等不同类型,用std::any可以统一存储;还有消息总线的消息载荷存储,不同的消息可能携带不同类型的业务数据,用std::any可以灵活承载。