一文速通C++17 std::any

std::any

概念

值类型

一般来说,C++是一门类型绑定和类型安全的语言。

  • 值对象:被声明为确定的类型,并且不能改变自身的类型。
  • 类型:定义了所有可能的操作、也定义了对象的行为。

std::any是一种在保证类型安全的基础上还能改变自身类型的值类型 —— 同时包含了值和值的类型

  • std::any可以持有任意类型的值
  • std::any知道自己当前持有的值是什么类型

声明std::any类型的对象时无需指明所有可能的类型,因此内含的值可以有任意的大小

  • 必须有可以在堆上分配内存的能力

    • 尽管如此,还是会尽量避免为小类型的值在堆上分配内存(例如 int)

    • 因此拷贝 std::any的开销一般都很大。更推荐以引用 或者 移动语义 传递对象 —— std::any支持部分 move语义。

  • 可以使用运行时检查来判断当前的值的类型。

目的

std::any的设计目标:

  • 提供一种类型安全且易于使用的方式来在运行时处理各种类型的数据
  • 任何错误的类型转换都会在运行时抛出异常。

std::any不是模版类,是用于任何 可拷贝构造类型的单个值 的类型安全容器

  • 只能容纳一个元素

  • 一种动态类型变量,可以存储任何类型的值

    • 基本数据类型(int,double,char,float…)

    • 复合数据类型(类、结构体)。

  • 值类型

    • 可查询类型:当前保存的是哪种类型的值
    • 可更改类型:更改后仍具有类型安全性。

缺点

  • 性能不如其他类型,运行时需要进行类型检查和类型转换。

  • 没有定义比较运算符(不能比较或者排序对象),哈希函数,value()成员函数。

  • 编译期不知道存储的类型 —— 不能使用泛型 lambda来独立于类型处理当前的值,处理新类型需要不断的人工加入。

  • 运行时也不知道存储的具体类型(只能依赖 RTTI进行比较),any_cast使用时需要显式地进行类型转换。

构建方式

构造函数

std::any的构造函数使用了 std::decay_t 会导致,诸如:

  • 数组T[N] 退化成T*
  • 函数 退化成 函数指针

所以实际使用中,建议跳过这一节,使用显式指定类型的方法(见下一章节的std::in_place_typestd::make_any)。

在声明此类型的对象时,不需要指定可能的类型。

std::any m_any_a; // m_any_a为空

当然,也可以赋初值

// 隐式推导
std::any m_any_a;
m_any_a= 42; // a有类型int, 值42std::any m_any_b = 4.3; // 有类型double, 值4.3
m_any_b = std::string{"hi"}; // 有类型std::string, 值"hi"std::any m_any_str = "wegrthweg"; //type : const char*// 手动创建对象
std::any a1{MyClass{6.0, 2.0}};

运行时可以配合 RTTI (Run-Time Type Information) 使用:

if (m_any_a.type() == typeid(std::string)) {std::string s = std::any_cast<std::string>(m_any_a);useString(s);
}
else if (m_any_a.type() == typeid(int)) {useInt(std::any_cast<int>(m_any_a));
}
operator=
//[1]
any& operator=( const any& rhs );
//[2]
any& operator=( any&& rhs ) noexcept;
//[3]
template<typename ValueType>
any& operator=( ValueType&& rhs );
指明类型
std::in_place_type

std::in_place_type<T>作为第一个参数

  1. 用于指明类型(可以覆盖原类型)
  2. 允许使用多个参数初始化对象

可以使用 in_place_type标记,以使内部值的类型和初始值的类型不同。

std::any m_any_int{std::in_place_type<int>, 420};
// std::in_place_type作为第一个参数
// - std::in_place_type + 可变参数
std::any a2{std::in_place_type<MyClass>, 6.0, 2.0};
// - std::in_place_type + 初始化列表
std::any a2{std::in_place_type<set<int>>, {3, 7, -1, -16, 1, 100}};auto func = [] (int x, int y) { return std::abs(x) < std::abs(y);};
std::any m_any_set{std::in_place_type<std::set<int,decltype(func)>>, {3, 7, -1, -16, 1, 100}, func
};

多参数初始化对象

auto sc = [] (int x, int y) {return std::abs(x) < std::abs(y);
};
std::any m_any_set{std::in_place_type <std::set<int, decltype(sc)>>, {4, 8,7,2, 0, 5}, sc};

指明类型后,甚至可以是lambda

std::any any_lambda {std::in_place_type<std::function<void(void)>>,[] { std::cout << "Lambda #1.\n"; }
};
std::make_any

显式指定初始化的类型

auto a0 = std::make_any<std::string>("Hello, std::any!\n"
);
std::cout << std::any_cast<std::string&>(a0);
std::cout << a0.type().name() << "\"\n";// 将 lambda 放入 std::any 使用 make_any
auto a_lambda = std::make_any<std::function<void(void)>>([] { std::cout << "Lambda #2.\n"; }
);
std::any_cast<std::function<void(void)>>(a_lambda)();

定义如下

template< class T, class... Args >
std::any make_any( Args&&... args );
// 等价于
return std::any(std::in_place_type<T>, std::forward<Args>(args)...);template< class T, class U, class... Args >
std::any make_any( std::initializer_list<U> il, Args&&... args );
// 等价于
return std::any(std::in_place_type<T>, std::initializer_list<U> il, std::forward<Args>(args)...);

操作

查询
成员函数用途返回值
has_value()查询对象是否含有值含值 true,不含值false
type()查询所含类型含值 typeid,不含值 typeid(void)

通过使用成员函数 type(),可以检查内含值的类型和某一个类型的 ID 是否相同。如果对象是空的,类

型 ID 将等于 typeid(void)。

std::any m_any_a = "hello"; // type()是const char*
if (m_any_a.type() == typeid(const char*)) { // truestd::cout << std::any_cast<const char*>(m_any_a);
} else if (m_any_a.type() == typeid(std::string)) {std::cout << std::any_cast<std::string>(m_any_a);std::cout << (m_any_a.has_value() ? std::any_cast<std::string>(a) : std::string("NIL"));
} else {// 若都不匹配std::cout << (m_any_a.type() == typeid(void));
}
访问 any_cast

要访问any包含的值,必须使用std::any_cast<>将any转换为真正的类型。

操作含义
T any_cast<T>(any& operand )将当前值转换为类型 T的值(如果类型不正确将抛出异常)
T* any_cast(any* operand )将当前值转换为类型 T的指针(如果类型不正确将返回 nullptr)
any值/引用

存在以下三个重载,支持以各种值类型返回。

// 存在以下三个重载
template< class T >
T any_cast( any operand ); // 返回 值的拷贝,会创建临时对象
template< class T >
T any_cast( any& operand ); // 更推荐转换为引用类型来避免创建临时对象
template< class T >
T any_cast( const any& operand ); // 常引用
// 不直接支持移动语义
std::string s("hello, world!");
// s = std::any_cast<std::string&&>(m_any_a); // 编译期error
std::any m_any_a = std::make_any<std::string>("Hello");
// 1.创建并返回临时对象
auto val = std::any_cast<std::string>(m_any_a);
// 2.返回引用(更推荐)
//  - 避免了创建临时对象
auto ref = std::any_cast<std::string&>(m_any_a);
//  - 可以修改该值
ref = "world"s;
// 3.返回常引用
//  - 避免了创建临时对象
//  - 可以避免误修改
auto cref = std::any_cast<const std::string&>(a); // read-access by reference// 支持移动语义
std::string s("hello, world!");
// s = std::any_cast<std::string&&>(m_any_a); // 编译期error
s = std::any_cast<std::string>(std::move(m_any_a)); // 可以使用
s = std::move(std::any_cast<std::string&>(a)); // 或者

如果转换失败,将抛出 std::bad_any_cast 异常。因此,在不缺点当前类型的情况下,最好捕获一下异常。

try {auto s = std::any_cast<std::string>(a);...
}
catch (std::bad_any_cast& e) {std::cerr << "EXCEPTION: " << e.what() << '\n';
}
any指针

为了避免异常处理,可以传递 any对象的地址。即:

为std::any对象的地址调用std::any_cast

template< class T >
const T* any_cast(const any* operand) noexcept;
template< class T >
T* any_cast( any* operand ) noexcept;

类型是否匹配

  • 匹配:返回相应的地址指针

  • 不匹配:返回nullptr

注意你使用 if-else链,而不是 switch语句。

// 返回指针
auto p = std::any_cast<std::string>(&m_any_a);
if (p) {}
// 使用新的带初始化的 if语句:
if (auto p = std::any_cast<std::string >(&m_any_a); p != nullptr) {}
if (auto p = std::any_cast<std::string >(&m_any_a)) {}
std::vector<std::any> v;
v.push_back(42);
std::string s = "hello";
v.push_back(s);
for (const auto& a : v) {if (auto pa = std::any_cast<const std::string >(&a)) {std::cout << "string: " << *pa << '\n';} else if (auto pa = std::any_cast<const int>(&a); pa != nullptr) {std::cout << "int: " << *pa << '\n';} else {std::cout << "unkown type\n";}
}
修改
操作含义
emplace<T>()赋予一个类型 T的新值
reset() 销毁 any类型(使对象变为空)
swap()交换两个对象的值
std::any m_any_a;
m_any_a = 42;		 // 含有int类型的值
m_any_a = "hello"; // 含有const char*类型的值
m_any_a.emplace<std::string>("hello"); // 含有std::string类型的值
m_any_a.emplace<std::complex<double>>(4.4, 5.5); // a含 有std::complex<double>类 型 的 值
m_any_a.reset(); // 清空对象
m_any_a = std::any{};
m_any_a = {};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/462022.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】宏封装的实用总结

在C语言的广阔天地中&#xff0c;宏&#xff08;Macro&#xff09;犹如一门神秘的内功&#xff0c;掌握它&#xff0c;你将能够以不变应万变&#xff0c;以简洁驾驭复杂。今天&#xff0c;我们将深入探讨C语言宏封装的高级技巧&#xff0c;并通过一系列案例&#xff0c;让你领略…

Latex中Reference的卷号加粗的问题

找到模板中的.bst文件&#xff0c;查找volume&#xff0c;修改如下 添加bold&#xff0c;卷号会加粗&#xff0c;去掉则正常

parted 磁盘分区

目录 磁盘格式磁盘分区文件系统挂载使用扩展 - parted、fdisk、gdisk 区别 磁盘格式 parted /dev/vdcmklabel gpt # 设置磁盘格式为GPT p # 打印磁盘信息此时磁盘格式设置完成&#xff01; 磁盘分区 开始分区&#xff1a; mkpart data_mysql # 分区名&…

基于Transformer的路径规划 - 第五篇 GPT生成策略_解码方法优化

上一篇&#xff1a;基于Transformer的路径规划 - 第四篇 GPT模型优化 在上一篇中&#xff0c;我尝试优化GPT路径生成模型&#xff0c;但没有成功。在随机生成的测试集上&#xff0c;路径规划成功率只有99%左右。而使用传统的路径规划算法&#xff0c;例如A*&#xff0c;路径规划…

【ROS的TF系统】

系列文章目录 TF系统简介 前面的章节实现了SLAM节点的建图功能&#xff1a; 激光雷达节点—> /scan话题 —>hector_mapping节点—> 地图数据话题/map 本期来实现SLAM节点的定位功能&#xff1a; TF&#xff08;TransForm&#xff09;主要描述的是两个坐标系的空间关…

Java中的线程安全问题(如果想知道Java中有关线程安全问题的基本知识,那么只看这一篇就足够了!)

前言&#xff1a;多线程编程已经广泛开始使用&#xff0c;其可以充分利用系统资源来提升效率&#xff0c;但是线程安全问题也随之出现&#xff0c;它直接影响了程序的正确性和稳定性&#xff0c;需要对其进行深入的理解与解决。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解…

奥数与C++小学四年级(第十八题 小球重量)

参考程序代码&#xff1a; #include <iostream> #include <vector>int main() {// 小球的重量std::vector<int> weights {1, 2, 3, 4, 5};// 用来存储可能的结果int a, b, c, d, e, x;// 穷举所有可能的 a, b, c, d, e 的组合for (int i 0; i < weight…

ESP32/ESP8266开发板单向一对多ESP-NOW无线通信

ESP32/ESP8266开发板单向一对多ESP-NOW无线通信 简介读取ESP32/ESP8266接收方Receiver的MAC地址ESP32/ESP8266发送方Sender程序ESP32/ESP8266接收方Receiver程序ESP-NOW通信验证总结 简介 本实验通过ESP-NOW无线通信协议实现多个ESP32/ESP 8266开发板向ESP32开发板发送数据。例…

Unity XR Interaction Toolkit 开发教程(2):导入 SDK【3.0 以上版本】

文章目录 &#x1f4d5;课程总结&#x1f4d5;安装 Unity 编辑器与打包模块&#x1f4d5;导入 OpenXR&#x1f4d5;导入 XR Interaction Toolkit&#x1f4d5;打包发布 获取完整课程以及答疑&#xff0c;工程文件下载&#xff1a; https://www.spatialxr.tech/ 视频试看链接&a…

直流电机在液压泵领域的应用

随着工业自动化的不断发展&#xff0c;液压技术已经成为现代工程中不可或缺的一部分。液压泵作为液压系统的核心部件&#xff0c;其性能直接关系到整个系统的效率和可靠性。近年来&#xff0c;直流电机因其独特的优势而逐渐应用于液压泵领域&#xff0c;为液压系统的提升与改进…

2024-10-29 商业分析-盗取他人游戏MOD牟利-记录

摘要&#xff1a; 2024-10-29 商业分析-盗取他人游戏MOD牟利-记录 事件&#xff1a; 【实锤】《真英雄》盗用本人《风林火山》mod地图售卖牟利&#xff01;_ryan_knight_12吧_百度贴吧 真英雄&#xff1f;&#xff1f;我从未见过如此厚颜无耻之人【ryan_knight_12吧】_百度贴吧…

利用钉钉与金蝶云星空进行付款单自动化集成

钉钉数据集成到金蝶云星空&#xff1a;付款申请单下推生成付款单的技术实现 在企业日常运营中&#xff0c;数据的高效流转和准确处理是业务顺利进行的关键。本文将分享一个具体的系统对接集成案例&#xff1a;如何将钉钉平台上的付款申请单&#xff0c;通过轻易云数据集成平台…

vscode 创建 vue 项目时,配置文件为什么收缩到一起展示了?

一、前言 今天用 vue 官方脚手架创建工程&#xff0c;然后通过 vscode 打开项目发现&#xff0c;配置文件都被收缩在一起了。就像下面这样 这有点反直觉&#xff0c;他们应该是在同一层级下的&#xff0c;怎么会这样&#xff0c;有点好奇&#xff0c;但是打开资源管理查看&…

001-Kotlin界面开发之Jetpack Compose Desktop学习路径

Compose Desktop学习之路 学习过程 理解Kotlin的基本语法 Compose Desktop采用Kotlin构建&#xff0c;因此对Kotlin的基本语法有很好的理解是必不可少的。你可以从官方的Kotlin文档开始。 用一句话概括&#xff0c;Kotlin是一种现代的、静态类型的编程语言&#xff0c;它结合…

Vue 组件基础(五)

一、Vue 组件的基础概念 组件(Component)是Vue最强大的功能之一。组件可以扩展HTML元素&#xff0c;封装可重用的代码。在较高层面上&#xff0c;组件是自定义元素&#xff0c;Vue的编译器为它添加特殊功能。每个组件负责一部分特定的任务&#xff0c;比如&#xff1a;显示一个…

RabbitMQ 存储机制

一、消息存储机制 不管是持久化的消息还是非持久化的消息都可以被写入到磁盘。持久化的消息在到达队列时就被写入到磁盘&#xff0c;非持久化的消息一般只保存在内存中&#xff0c;在内存吃紧的时候会被换入到磁盘中&#xff0c;以节省内存空间。这两种类型的消息的落盘处理都…

随机性、熵与随机数生成器:解析伪随机数生成器(PRNG)和真随机数生成器(TRNG)

随机性在诸多领域中扮演着至关重要的角色,涵盖密码学、仿真和机器学习等方面。因为随机性为无偏决策、不可预测序列和安全加密提供了基础。然而生成随机数是一项复杂的任务,理解伪随机数生成(pseudo-random number generation, PRNG)与真随机数生成(true random number generat…

从零开始点亮一个LED灯 —— keil下载、新建工程、版本烧录、面包板使用、实例代码

一、keil下载 参考视频&#xff1a;Keil5安装教程视频 (全套资料51和32皆可用Keil5编译设置)_哔哩哔哩_bilibili 视频内容包括下载链接、安装教程、库导入&#xff0c;非常详细&#xff01; 二、新建工程 2.1.使用stm32CubeMX新建工程 10. 使用STM32CubeMX新建工程 — [野…

嵌入式硬件电子电路设计(三)电源电路之负电源

引言&#xff1a;在对信号线性度放大要求非常高的应用需要使用双电源运放&#xff0c;比如高精度测量仪器、仪表等;那么就需要给双电源运放提供正负电源。 目录 负电源电路原理 负电源的作用 如何产生负电源 负电源能作功吗&#xff1f; 地的理解 负电压产生电路 BUCK电…

互斥量的使用

官方的描述 互斥量主要是对于共享资源的保护 其中参数要注意 osMutexRecursive&#xff1a;//递归互斥量 互斥锁嵌套属性&#xff0c;同一个线程可以在不锁定自身的情况下多次使用互斥锁。每当拥有互斥锁的线程获得互斥锁时&#xff0c;锁计数就会增加。互斥锁也必须被释放多次…