std::any

一、简介

std::any 可以储存任何可拷贝构造可销毁的类型的对象。

struct test
{
    test(int a,int b){}
};

int main(int argc, char *argv[])
{
    std::any a = 1;
    qDebug() << a.type().name();
    a = 3.14;
    qDebug() << a.type().name();
    a = true;
    qDebug() << a.type().name();
    a = test(1,2);
    qDebug() << a.type().name();
}

QObject 类型是不可拷贝的,直接将此类对象赋给 std::any 就会报错:

要把 QObject 赋给 std::any 应该赋指针:

二、相关函数

1、std::any_cast

在类型匹配的情况下,从 std::any 中获取存储的值,并将其转换为目标类型。

int main(int argc, char *argv[])
{
    std::any storage = 3.14;

    double d = std::any_cast<double>(storage);
    qDebug() << d;

    double* ptr = std::any_cast<double>(&storage);
    if (ptr)
    {
        qDebug() << *ptr;
    }
}

要注意如果类型不匹配会抛出 std::bad_any_cast 异常。

2、std::make_any

在运行时构建 std::any 类型的对象,并以推导模板参数的方式自动将输入参数转换为 std::any

这是两种方式:

std::any val = std::make_any<int>(23);
std::any val = 23;

二者的区别:

  • std::make_any<int>(23):使用 std::make_any 函数并指定模板参数为 int,通过调用 int 类型的构造函数创建一个临时的 int 对象,然后将该对象存储在 std::any 中。
  • std::any val = 23:直接将整数值 23 赋给 std::any 变量 val,此时编译器会自动推导出 std::any 对象的类型,并调用相应的构造函数来存储该值。

两者的区别在于对象的构造方式。使用 std::make_any 可以显式指定对象的类型,并通过调用构造函数来创建对象。

3、std::any::emplace

它是 std::any 成员函数,用于在 std::any 中就地构造存储的对象。

它可以在运行时指定对象的类型,并通过提供构造函数的参数,在 std::any 中构造出一个新的对象。

与直接赋值相比更加显示。

int main(int argc, char *argv[])
{
    std::any storage1 = 2.2;
    storage1 = 23;
    qDebug()<<storage1.type().name();   //int

    std::any storage2 = 2.2;
    storage2.emplace<qint8>(23);
    qDebug()<<storage2.type().name();   //signed char
}

4、std::any::has_value

std::any 的成员函数,用于检查 std::any 对象是否存储了一个值。

int main(int argc, char *argv[])
{
    std::any storage;
    qDebug() << storage.has_value(); // false
    storage = 42;
    qDebug() << storage.has_value() ; // true
}

5、std::any::reset

std::any 的成员函数,用于将 std::any 对象重置为空,释放其持有的值。

int main(int argc, char *argv[])
{
    std::any storage = 42;
    qDebug() << storage.has_value(); // true
    storage.reset();
    qDebug() << storage.has_value(); // false
}

6、std::any::type

std::any 的成员函数,如果对象非空则返回 std::any 对象的 typeidstd::type_info),空则返回 typeid(void)

<think>好的,我现在需要详细解释C++17中的std::any。用户之前问过关于函数指针和map的问题,现在转向std::any,可能是想了解类型安全的通用容器或替代方案。 首先,回顾std::any的基本概念。它属于C++17的类型擦除容器,用于存储任意类型的数据,同时保持类型安全。需要比较std::any与之前的void*或模板的区别,强调类型安全的特点。 然后,分析std::any的核心机制,包括内部存储、类型擦除、访问方式等。可能需要解释如何通过any_cast进行类型安全的访问,以及错误处理(比如bad_any_cast异常)。 接着,考虑用户可能的实际应用场景。比如在配置管理、回调函数存储、异构容器等情况下使用std::any。需要举例说明,例如存储不同类型的参数,或者实现插件系统。 还需要注意std::any的限制,比如性能开销、不能存储引用、不可复制类型的问题。这部分对于用户正确使用很重要,避免误用。 另外,对比其他类似工具,如std::variant和继承多态。说明在什么情况下选择std::any更合适,例如类型集合未知或需要极大灵活性时。 可能用户之前的问题涉及到函数参数类型不匹配,用std::any作为替代方案是否可行。需要指出在这种情况下,使用std::any需要配合类型转换,可能增加复杂性,而继承方案可能更直接,但std::any提供更大的灵活性。 还要注意示例代码的正确性,比如如何正确构造std::any对象,如何使用any_cast获取值,包括指针版本和引用版本的区别,以及异常处理的方式。 最后,总结std::any的适用场景和最佳实践,帮助用户决定何时使用它,以及使用时需要注意的事项。</think>以下是对C++17中`std::any`的详细解析,采用分步说明和代码示例: --- ### $\textcolor{blue}{一、std::any核心特性}$ **类型安全**的通用值容器,可存储**任意可拷贝类型**,通过`type()`和`any_cast`实现类型操作 #### 与旧方案对比 | 方案 | 类型安全 | 空值检测 | 存储限制 | 异常机制 | |---------------|----------|----------|-------------------|----------------| | `void*` | ❌ | ❌ | 需手动管理内存 | 易导致未定义行为| | `template` | ✅ | ✅ | 需提前确定类型 | 编译期错误 | | **`std::any`**| ✅ | ✅ | 任意可拷贝类型 | 抛出标准异常 | --- ### $\textcolor{blue}{二、核心操作接口}$ #### 1. 构造与赋值 ```cpp std::any a1; // 空any std::any a2 = 42; // 存储int std::any a3 = std::string("hello"); a3.emplace<float>(3.14f); // 原地构造新值 ``` #### 2. 值访问 ```cpp try { int val = std::any_cast<int>(a2); // 成功返回42 float f = std::any_cast<float>(a3); // 抛出bad_any_cast } catch(const std::bad_any_cast& e) { std::cerr << e.what() << std::endl; } // 安全指针访问(不抛异常) if(auto* pstr = std::any_cast<std::string>(&a3)) { std::cout << *pstr; // 输出"hello" } ``` #### 3. 类型查询 ```cpp if(a2.type() == typeid(int)) { // 类型比对 std::cout << "存储int类型\n"; } ``` --- ### $\textcolor{blue}{三、实现原理剖析}$ #### 内存管理策略 ```mermaid graph LR A[std::any对象] --> B[小型缓冲区优化 SBO] A --> C[堆内存分配] B -->|存储<=sizeof(void*)*3| D[直接存储值] C -->|大对象| E[动态分配存储] ``` #### 类型擦除机制 通过内部`Manager`函数指针实现操作: - `copy`:深拷贝 - `destroy`:析构对象 - `type`:返回type_info --- ### $\textcolor{blue}{四、典型应用场景}$ #### 1. 动态配置存储 ```cpp std::unordered_map<std::string, std::any> config; config["resolution"] = 1920x1080; // 存储pair<int,int> config["title"] = std::string("Demo"); config["fov"] = 90.0f; // 读取时类型检查 if(config["fov"].type() == typeid(float)) { Renderer.setFOV(std::any_cast<float>(config["fov"])); } ``` #### 2. 跨类型回调 ```cpp using Callback = std::function<std::any(std::any)>; std::map<int, Callback> handlers; // 注册处理函数 handlers[1] = [](std::any param) -> std::any { return std::any_cast<int>(param) * 2; }; // 调用 std::any input = 21; auto result = handlers[1](input); // 返回42 ``` --- ### $\textcolor{red}{五、重要限制与陷阱}$ 1. **不可存储引用**:`std::any`只能存储实例,需用`std::reference_wrapper` ```cpp int x = 10; std::any a = std::ref(x); // 存储引用包装器 ``` 2. **移动语义**:转移所有权后源any变为空 ```cpp std::any a1 = createResource(); std::any a2 = std::move(a1); // a1现在为空 ``` 3. **性能损耗**:类型检查与动态分配带来开销,高频调用场景慎用 --- ### $\textcolor{blue}{六、与相关工具对比}$ | 特性 | std::any | std::variant | 继承多态 | |---------------------|-------------------|--------------------|------------------| | 存储类型数量 | 无限 | 模板指定有限类型 | 继承链类型 | | 类型安全 | ✅ | ✅ | ✅ | | 运行时类型识别(RTTI)| 必需 | 不需要 | 需要dynamic_cast | | 内存布局 | 可能动态分配 | 静态确定 | 虚表指针 | | 典型应用 | 通用型容器 | 有限类型集合 | 类层次结构 | --- ### $\textcolor{green}{七、最佳实践建议}$ 1. **优先用于类型不确定场景**:如反序列化、脚本引擎变量存储 2. **避免高频使用**:性能敏感代码改用`std::variant`或模板 3. **配合类型检查**:使用`type()`或`any_cast`指针版本防御性编程 4. **RAII扩展**:封装自定义any类型实现资源自动管理 ```cpp class SafeAny { std::any data_; void(*deleter_)(void*); public: template<typename T> SafeAny(T&& val) : data_(std::forward<T>(val)) { deleter_ = [](void* p) { delete static_cast<T*>(p); }; } ~SafeAny() { if(data_.has_value()) deleter_(&data_); } }; ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值