C++三剑客之std::any(一) : 使用

相关系列文章

C++三剑客之std::any(一) : 使用

C++之std::tuple(一) : 使用精讲(全)

C++三剑客之std::variant(一) : 使用

C++三剑客之std::variant(二):深入剖析​​​​​​​

目录

1.概述

2.构建方式

2.1.构造函数

2.2.std::make_any

2.3.operator=分配新值

3.访问值std::any_cast

4.修改器

4.1.emplace

4.2.reset

4.3.swap

5.观察器

5.1.has_value

5.2.type

6.总结


1.概述

        C++17的三剑客分别是std::optionalstd::anystd::vairant。今天主要讲std::any。std::any类用于任何可拷贝构造类型的单个值的类型安全容器。在头文件<any>中,c++标准库定义了类std::any。

namespace std {
class any;
}

       从上面的定义可以看出std::any不是模版类,而是一种很特殊的容器,它只能容纳一个元素,但这个元素可以是任意的类型,可以是基本数据类型(int,double,char,float...)也可以是复合数据类型(类、结构体)。

        std: any是一种值类型,它能够更改其类型,同时仍然具有类型安全性。也就是说,对象可以保存任意类型的值,但是它们知道当前保存的值是哪种类型。在声明此类型的对象时,不需要指定可能的类型。

2.构建方式

2.1.构造函数

默认情况下,std::any的初始值为空。如:

std::any  a;

也可以赋初值,初始化对象std::any, 如:

std::any a = 32; //type: int
std::any b = "wegrthweg"; type : const chr*

要保存与初始值类型不同的类型,必须使用in_place_type标记:

std::any a{std::in_place_type<int>, 420};
std::any b{std::in_place_type<std::string>, "asdfbsrghtr34"};

即使传递给in_place_type的类型也会退化。下面的声明包含一个const char*:

std::any a{std::in_place_type<const char[6]>, "12345"};

要通过多个参数初始化可选对象,必须创建该对象或将std::in_place_type添加为第一个参数(不能推断包含的类型):

std::any a1{std::complex{6.0, 2.0}};
std::any a2{std::in_place_type<std::complex<double>>, 6.0, 2.0};

甚至可以传递一个初始化器列表,后面跟着附加的参数:

auto func = [] (int x, int y) { return std::abs(x) < std::abs(y);};std::any a{std::in_place_type<std::set<int,decltype(func)>>, {3, 7, -1, -16, 1, 100}, func};

2.2.std::make_any

std::make_any的定义如下:

//[1]
template< class T, class... Args >
std::any make_any( Args&&... args );
//[2]
template< class T, class U, class... Args >
std::any make_any( std::initializer_list<U> il, Args&&... args );

构造含 T 类型对象的 any 对象,传递提供的参数给 T 的构造函数。

1) 等价于 return std::any(std::in_place_type<T>, std::forward<Args>(args)...);

2) 等价于 return std::any(std::in_place_type<T>, il, std::forward<Args>(args)...);

std::make_any必须显式指定初始化的类型(如果只传递一个参数,则不会推导出初始化的类型),如:

#include <any>
#include <complex>
#include <functional>
#include <iostream>
#include <string>int main()
{auto a0 = std::make_any<std::string>("Hello, std::any!\n");auto a1 = std::make_any<std::complex<double>>(0.1, 2.3);std::cout << std::any_cast<std::string&>(a0);std::cout << std::any_cast<std::complex<double>&>(a1) << '\n';using lambda = std::function<void(void)>;// 把 lambda 放入 std::any。尝试 #1 (失败)。std::any a2 = [] { std::cout << "Lambda #1.\n"; };std::cout << "a2.type() = \"" << a2.type().name() << "\"\n";// any_cast 转型到 <void(void)> 但实际类型不是// std::function ……,而是 ~ main::{lambda()#1} ,且它对// 每个 lambda 唯一。所以这会抛出……try {std::any_cast<lambda>(a2)();}catch (std::bad_any_cast const& ex) {std::cout << ex.what() << '\n';}// 将 lambda 放入 std::any 中。尝试 #2 (成功)auto a3 = std::make_any<lambda>([] { std::cout << "Lambda #2.\n"; });std::cout << "a3.type() = \"" << a3.type().name() << "\"\n";std::any_cast<lambda>(a3)();
}

输出:

Hello, std::any!
(0.1,2.3)
a2.type() = "Z4mainEUlvE_"
bad any_cast
a3.type() = "St8functionIFvvEE"
Lambda #2.

2.3.operator=分配新值

operator=的定义如下:

//[1]
any& operator=( const any& rhs );
//[2]
any& operator=( any&& rhs ) noexcept;
//[3]
template<typename ValueType>
any& operator=( ValueType&& rhs );

1) 通过复制 rhs 的状态赋值,如同用 any(rhs).swap(*this)。

2) 通过移动 rhs 的状态赋值,如同用 any(std::move(rhs)).swap(*this)。赋值后 rhs 留在有效但未指定的状态。

3) 以 rhs 的类型和值赋值,如同用 any(std::forward<ValueType>(rhs)).swap(*this)。此重载只有在 std::decay_t<ValueType> 与 any 不是同一类型且 std::is_copy_constructible_v<std::decay_t<ValueType>> 为 true 时才会参与重载决议。std::decay_t<ValueType> 必须满足可复制构造条件。示例如下:

std::any a = 1223;
std::any b = "2222222";
a = b; //[1]
b = 6.34655; //[3]
a = std::make_any<std::complex>(6.0, 2.0); //[2]

上面代码执行a = b调用的是第1个赋值函数,b=6.34655调用的是第3个赋值函数,a = std::make_any<std::complex>(6.0, 2.0) 是调用的第2个赋值函数。

3.访问值std::any_cast

std::any_cast的定义:

//[1]
template< class T >
T any_cast( const any& operand );
//[2]
template< class T >
T any_cast( any& operand );
//[3]
template< class T >
T any_cast( any&& operand );
//[4]
template< class T >
const T* any_cast( const any* operand ) noexcept;
//[5]
template< class T >
T* any_cast( any* operand ) noexcept;

要访问包含的值,必须使用std::any_cast<>将其转换为其类型。将该值转换为一个字符串,有几个选项: 

std::any_cast<std::string>(a) // yield copy of the value
std::any_cast<std::string&>(a); // write value by reference
std::any_cast<const std::string&>(a); // read-access by reference

在这里,如果转换失败,将抛出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';
}

注意,std::any_cast<>创建了一个传递类型的对象。如果将std::string作为模板参数传递给std::any_cast<>,它将创建一个临时string(一个prvalue),然后用它初始化新对象。如果没有这样的初始化,通常最好转换为引用类型,以避免创建临时对象:

std::cout << std::any_cast<const std::string&>(a);

要修改该值,需要转换为对应的引用类型:

std::any_cast<std::string&>(a) = "2134y56";

根据上面的第4或5个转换函数定义还可以为std::any对象的地址调用std::any_cast。在这种情况下,如果类型匹配,则强制转换返回相应的地址指针;如果不匹配,则返回nullptr:

auto p = std::any_cast<std::string>(&a);
if (p) {...
}

下面再看一个例子:

#include <any>
#include <iostream>
#include <string>
#include <type_traits>
#include <utility>int main()
{// 简单示例auto a1 = std::any(12);std::cout << "1) a1 是 int:" << std::any_cast<int>(a1) << '\n';try{auto s = std::any_cast<std::string>(a1); // 抛出}catch (const std::bad_any_cast& e){std::cout << "2) " << e.what() << '\n';}// 指针示例if (int* i = std::any_cast<int>(&a1))std::cout << "3) a1 是 int:" << *i << '\n';else if (std::string* s = std::any_cast<std::string>(&a1))std::cout << "3) a1 是 std::string:" << *s << '\n';elsestd::cout << "3) a1 是另一类型,或者没有设置\n";// 进阶示例a1 = std::string("hello");auto& ra = std::any_cast<std::string&>(a1); //< 引用ra[1] = 'o';std::cout << "4) a1 是字符串:"<< std::any_cast<std::string const&>(a1) << '\n'; //< const 引用auto s1 = std::any_cast<std::string&&>(std::move(a1)); //< 右值引用// 注意:“s1” 是移动构造的 std::string:static_assert(std::is_same_v<decltype(s1), std::string>);// 注意:“a1” 中的 std::string 被置于合法但未指定的状态std::cout << "5) a1.size():"<< std::any_cast<std::string>(&a1)->size() //< 指针<< '\n'<< "6) s1:" << s1 << '\n';
}

输出:

1) a1 是 int:12
2) bad any_cast
3) a1 是 int:12
4) a1 是 string:hollo
5) a1.size():0
6) s1:hollo

4.修改器

4.1.emplace

更改所含对象,直接构造新对象,示例如下:

#include <algorithm>
#include <any>
#include <iostream>
#include <string>
#include <vector>class Star
{std::string name;int id;public:Star(std::string name, int id) : name { name }, id { id }{std::cout << "Star::Star(string, int)\n";}void print() const{std::cout << "Star{ \"" << name << "\" : " << id << " };\n";}
};auto main() -> int
{std::any celestial;// (1) emplace( Args&&... args );celestial.emplace<Star>("Procyon", 2943);const auto* star = std::any_cast<Star>(&celestial);star->print();std::any av;// (2) emplace( std::initializer_list<U> il, Args&&... args );av.emplace<std::vector<char>>({ 'C', '+', '+', '1', '7' } /* 无参数 */ );std::cout << av.type().name() << '\n';const auto* va = std::any_cast<std::vector<char>>(&av);std::for_each(va->cbegin(), va->cend(), [](char const& c) { std::cout << c; });std::cout << '\n';
}

输出:

Star::Star(string, int)
Star{ "Procyon" : 2943 };
St6vectorIcSaIcEE
C++17

4.2.reset

销毁所含对象。

4.3.swap

交换两个std::any。

5.观察器

5.1.has_value

检查对象是否含有值,若实例含值则为 true ,否则为 false 。示例如下:

#include <any>
#include <iostream>
#include <string>int main()
{std::boolalpha(std::cout);std::any a0;std::cout << "a0.has_value(): " << a0.has_value() << "\n";std::any a1 = 42;std::cout << "a1.has_value(): " << a1.has_value() << '\n';std::cout << "a1 = " << std::any_cast<int>(a1) << '\n';a1.reset();std::cout << "a1.has_value(): " << a1.has_value() << '\n';auto a2 = std::make_any<std::string>("Milky Way");std::cout << "a2.has_value(): " << a2.has_value() << '\n';std::cout << "a2 = \"" << std::any_cast<std::string&>(a2) << "\"\n";a2.reset();std::cout << "a2.has_value(): " << a2.has_value() << '\n';
}

输出:

a0.has_value(): false
a1.has_value(): true
a1 = 42
a1.has_value(): false
a2.has_value(): true
a2 = "Milky Way"
a2.has_value(): false

5.2.type

查询所含类型,若实例非空则为所含值的 typeid ,否则为 typeid(void) 。示例如下:

#include <type_traits>
#include <any>
#include <functional>
#include <iomanip>
#include <iostream>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <vector>template<class T, class F>
inline std::pair<const std::type_index, std::function<void(std::any const&)>>to_any_visitor(F const &f)
{return {std::type_index(typeid(T)),[g = f](std::any const &a){if constexpr (std::is_void_v<T>)g();elseg(std::any_cast<T const&>(a));}};
}static std::unordered_map<std::type_index, std::function<void(std::any const&)>>any_visitor {to_any_visitor<void>([]{ std::cout << "{}"; }),to_any_visitor<int>([](int x){ std::cout << x; }),to_any_visitor<unsigned>([](unsigned x){ std::cout << x; }),to_any_visitor<float>([](float x){ std::cout << x; }),to_any_visitor<double>([](double x){ std::cout << x; }),to_any_visitor<char const*>([](char const *s){ std::cout << std::quoted(s); }),// ……添加更多你的类型的特化……};inline void process(const std::any& a)
{if (const auto it = any_visitor.find(std::type_index(a.type()));it != any_visitor.cend()) {it->second(a);} else {std::cout << "Unregistered type "<< std::quoted(a.type().name());}
}template<class T, class F>inline void register_any_visitor(F const& f)
{std::cout << "Register visitor for type "<< std::quoted(typeid(T).name()) << '\n';any_visitor.insert(to_any_visitor<T>(f));
}auto main() -> int
{std::vector<std::any> va { {}, 42, 123u, 3.14159f, 2.71828, "C++17", };std::cout << "{ ";for (const std::any& a : va) {process(a);std::cout << ", ";}std::cout << "}\n";process(std::any(0xFULL)); //< 反注册 "y" 的类型( unsigned long long )std::cout << '\n';register_any_visitor<unsigned long long>([](auto x) {std::cout << std::hex << std::showbase << x; });process(std::any(0xFULL)); //< OK : 0xfstd::cout << '\n';
}

输出: 

{ {}, 42, 123, 3.14159, 2.71828, "C++17", }
Unregistered type "y"
Register visitor for type "y"
0xf

6.总结

        std::any是一个动态类型变量,可以存储任何类型的值。它是由C++17引入的一个新特性。std::any的设计目标是提供一种类型安全且易于使用的方式来在运行时处理各种类型的数据,因为任何错误的类型转换都会在运行时抛出异常。然而,std::any也有一些缺点。首先,因为std::any在运行时并不知道它存储的数据的具体类型,所以我们需要显式地进行类型转换。这可能会使代码变得复杂和难以理解。其次,std::any的性能可能不如其他类型,因为它需要在运行时进行类型检查和类型转换。

        总之,只要掌握了这些std::any的特性,明白了它的使用场景,才能灵活的使用std::any。

参考:std::any - cppreference.com

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

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

相关文章

猫头虎分享已解决Bug || Go Error: imported and not used: ‘fmt‘

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

EMC学习笔记(二十一)降低EMI的PCB设计指南(一)

降低EMI的PCB设计指南&#xff08;一&#xff09; 1.概述2.射频3.连接器与过孔元件4.静态引脚和动态引脚和输入5.基本回路6.差模与共模 tips&#xff1a;资料主要来自网络&#xff0c;仅供学习使用。 1.概述 印刷电路板(PCB)的一般布局准则&#xff0c;基本上都有相对的文件进…

Phobos捆绑某数控软件AdobeIPCBroker组件定向勒索

前言 Phobos勒索病毒最早于2019年被首次发现并开始流行起来&#xff0c;该勒索病毒的勒索提示信息特征与CrySiS(Dharma)勒索病毒非常相似&#xff0c;但是两款勒索病毒的代码特征却是完全不一样&#xff0c;近日笔者在逛某开源恶意软件沙箱的时候发现了一款Phobos勒索病毒捆绑…

ad18学习笔记十八:如何放置丝印层敷铜?

我画板的时候&#xff0c;需要把板卡顶面丝印层的一个矩形区域&#xff0c;画成白色&#xff0c;但是这个区域内有好几个焊盘&#xff0c;丝印涂色的地方需要避开这几个焊盘&#xff0c;我觉得不能简单的在丝印层画一个矩形完事&#xff0c;最好让丝印层的这个区域&#xff0c;…

Docker容器化K8s集群部署教程(一键部署sheel脚本)

本文通过脚本&#xff0c;可以快速地部署和配置Kubernetes环境&#xff0c;省去了各插件手动部署、配置的繁琐过程。 先看最终结果&#xff1a; [rootlocalhost home]# kubectl get node NAME STATUS ROLES AGE VERSION k8smaster Ready control-p…

计网——运输层、端口号

目录 运输层 1 进程之间的通信 运输层的作用 屏蔽作用 可靠信道与不可靠信道 2 运输层的两个主要协议 3 运输层的端口 端口号 (protocol port number) 软件端口 硬件端口 TCP/IP 运输层端口的标志 两大类、三种类型的端口 常用的熟知端口 运输层 1 进程之间的通信 …

【Spring源码解读!底层原理进阶】【下】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;底层原理高级进阶》 &#x1f680…

「深度学习」循环神经网络RNN

一、序列模型的例子 二、数学符号定义 X^{(i)<t>}&#xff1a;训练样本 i 的输入序列的第 t 个元素。 T_{X}^{i}&#xff1a;训练样本 i 的输入序列的长度。 Y^{(i)<t>}&#xff1a;训练样本 i 的输出序列的第 t 个元素。 T_{Y}^{i}&#xff1a;训练样本 i 的输…

[大厂实践] Netflix容器平台内核panic可观察性实践

在某些情况下&#xff0c;K8S节点和Pod会因为出错自动消失&#xff0c;很难追溯原因&#xff0c;其中一种情况就是发生了内核panic。本文介绍了Netflix容器平台针对内核panic所做的可观测性增强&#xff0c;使得发生内核panic的时候&#xff0c;能够导出信息&#xff0c;帮助排…

Linux(二)

远程登录 Linux大多应用于服务器&#xff0c;而服务器不可能像PC一样放在办公室&#xff0c;它们是放在IDC机房的&#xff0c;所以我平时登录linux系统都是通过远程登录的。 Linux系统中是通过ssh服务实现的远程登录功能。默认ssh服务开启了22端口&#xff0c;而且当我们安装…

计划任务功能优化,应用商店上架软件超过100款,1Panel开源面板v1.9.6发布

2024年2月7日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel正式发布v1.9.6版本。 在v1.9.5和v1.9.6这两个小版本中&#xff0c;1Panel针对计划任务等功能进行了多项优化和Bug修复。此外&#xff0c;1Panel应用商店新增了3款应用&#xff0c;上架精选软件应用超过1…

基于大规模连续多目标优化的共轭梯度-进化集成算法

声明&#xff1a;文章题目字数有限&#xff0c;翻译水平有限&#xff0c;仅供参考&#xff01; 原题目: Integrating Conjugate Gradients Into Evolutionary Algorithms for Large-Scale Continuous Multi-Objective Optimization 引&#xff1a;这么久了&#xff0c;又捡起…

C++之std::tuple(一) : 使用精讲(全)

相关系列文章 C之std::tuple(一) : 使用精讲(全) C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二)&#xff1a;深入剖析 深入理解可变参数(va_list、std::initializer_list和可变参数模版) std::apply源码分析 目录 1.简介 2.std::ignore介绍 3.创建元组 3.1.…

spring boot打完jar包后使用命令行启动,提示xxx.jar 中没有主清单属性

在对springBoot接口中间件开发完毕后&#xff0c;本地启动没有任何问题&#xff0c;在使用package命令打包也没异常&#xff0c;打完包后使用命令行&#xff1a;java -jar xxx.jar启动发现报异常&#xff1a;xxx.jar 中没有主清单属性&#xff0c;具体解决方法如下&#xff1a;…

华为云账号注销之后账号下的域名丢了怎么办?记录一次域名转移权限的经历

背景 我之前在阿里云上买了个域名&#xff0c;有效期10年的&#xff0c;然后在2023年1月末转移到华为云了&#xff0c;交了一年的域名费用&#xff0c;买了一个一年的华为云服务器 一年之后&#xff0c;华为云的服务器也到期了&#xff0c;我就想着参加新用户计划&#xff0c…

vscode 无法远程连接waiting the server log

使用版本 报错信息 相关日志 [17:32:59.765] > Waiting for server log... [17:32:59.801] > Waiting for server log... [17:32:59.831] > > * > * Visual Studio Code Server > * > * By using the software, you agree to > * the Visual Studio…

[算法前沿]--059-大语言模型Fine-tuning踩坑经验之谈

前言 由于 ChatGPT 和 GPT4 兴起,如何让人人都用上这种大模型,是目前 AI 领域最活跃的事情。当下开源的 LLM(Large language model)非常多,可谓是百模大战。面对诸多开源本地模型,根据自己的需求,选择适合自己的基座模型和参数量很重要。选择完后需要对训练数据进行预处…

MySQL篇----第十四篇

系列文章目录 文章目录 系列文章目录前言一、MySQL 数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化?二、锁的优化策略三、索引的底层实现原理和优化四、什么情况下设置了索引但无法使用前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽…

BGP协议

1.BGP相关概念 1.1 BGP的起源 不同自治系统&#xff08;路由域&#xff09;间路由交换与管理的需求推动了EGP的发展&#xff0c;但是EGP的算法简单&#xff0c;无法选路&#xff0c;从而被BGP取代。 自治系统&#xff1a;&#xff08;AS&#xff09; IGP&#xff1a;自治系统…

【Linux】gdb调试与make/makefile工具

目录 导读 1. make/Makefile 1.1 引入 1.2 概念 1.3 语法规则 1.4 示例 2. Linux调试器-gdb 2.1 引入 2.2 概念 2.3 使用 导读 我们在上次讲了Linux编辑器gcc\g的使用&#xff0c;今天我们就来进一步的学习如何调试&#xff0c;以及makefile这个强大的工具。 1. mak…