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.mzph.cn/news/674101.shtml

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

相关文章

【python绘图】爱心、樱花树、饼图、折线图、雷达图

一、爱心 import turtledef curvemove():for i in range(200):turtle.speed(0)turtle.right(1) # 光标向右偏1度turtle.forward(1)# 前进1pxturtle.penup() turtle.goto(0, -70) turtle.pendown()turtle.color(red) turtle.begin_fill() turtle.left(140) turtle.forward(111…

【从Python基础到深度学习】1. Python PyCharm安装及激活

前言&#xff1a; 为了帮助大家快速入门机器学习-深度学习&#xff0c;从今天起我将用100天的时间将大学本科期间的所学所想分享给大家&#xff0c;和大家共同进步。【从Python基础到深度学习】系列博客中我将从python基础开始通过知识和代码实践结合的方式进行知识的分享和记…

【递归】【前序中序后序遍历】【递归调用栈空间与二叉树深度有关】【斐波那契数】Leetcode 94 144 145

【递归】【前序中序后序遍历】【递归调用栈空间与二叉树深度有关】Leetcode 94 144 145 1.前序遍历&#xff08;递归&#xff09; preorder2.中序遍历&#xff08;递归&#xff09;inorder3.后序遍历&#xff08;递归&#xff09;postorder4. 斐波那契数 ---------------&…

学习 Redis 基础数据结构,不讲虚的。

学习 Redis 基础数据结构&#xff0c;不讲虚的。 一个群友给我发消息&#xff0c;“该学的都学了&#xff0c;怎么就找不到心意的工作&#xff0c;太难了”。 很多在近期找过工作的同学一定都知道了&#xff0c;背诵八股文已经不是找工作的绝对王牌。企业最终要的是可以创造价…

【服务器数据恢复】HP EVA虚拟化磁盘阵列数据恢复原理方案

EVA存储结构&原理&#xff1a; EVA是虚拟化存储&#xff0c;在工作过程中&#xff0c;EVA存储中的数据会不断地迁移&#xff0c;再加上运行在EVA上的应用都比较繁重&#xff0c;磁盘负载高&#xff0c;很容易出现故障。EVA是通过大量磁盘的冗余空间和故障后rss冗余磁盘动态…

实例分割论文阅读之:《Mask Transfiner for High-Quality Instance Segmentation》

1.摘要 两阶段和基于查询的实例分割方法取得了显著的效果。然而&#xff0c;它们的分段掩模仍然非常粗糙。在本文中&#xff0c;我们提出了一种高质量和高效的实例分割Mask Transfiner。我们的Mask Transfiner不是在规则的密集张量上操作&#xff0c;而是将图像区域分解并表示…

TCP相关知识点

TCP相关知识点 参考&#xff1a; 《计算机网络》 (建议收藏)TCP协议灵魂之问&#xff0c;巩固你的网路底层基础 关于 TCP 三次握手和四次挥手&#xff0c;满分回答在此 (值得看) TCP处于网络体系结构中的运输层。 运输层主要为应用进程提供端到端的逻辑通信&#xff0c;然后对…

zzzzzzzzzzzzzzzzz

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 磁盘满的本质分析 专栏&#xff1a;《Linux从小白到大神》 | 系统学习Linux开发、VIM/GCC/GDB/Make工具…

Snipaste使用

今天推荐一款好用的截图、贴图软件工具&#xff0c;名字叫Snipaste&#xff0c;以下是官方介绍的截图 软件官方下载地址&#xff1a; Snipaste 下载 1、截图功能 2、标注 3、开发中的使用 有时候在开发中需要临时把一些任务规则信息&#xff0c;放在代码编辑器旁边进行参考&am…

linux之wsl2安装远程桌面

0. 安装后的效果 1. wsl中打开terminal并安装库 sudo apt-get purge xrdp sudo apt install -y xrdp sudo apt install -y xfce4 sudo apt install -y xfce4-goodies 2.优化显示 sudo sed -i s/max_bpp32/#max_bpp32\nmax_bpp128/g /etc/xrdp/xrdp.ini sudo sed -i s/xserverbp…

安全名词解析-水坑攻击、鱼叉攻击、钓鲸攻击

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 水坑攻击02 鱼叉攻击03 钓鲸攻击 01 水坑攻击 水坑攻击&#xff08;Watering Hole Attack&#xff09;是一种针对特定目标的网络攻击方式。顾名思义&#xff0c;是在受害者必经之路设置了一个“水坑…

百亿规模京东实时浏览记录系统的设计与实现

百亿规模京东实时浏览记录系统的设计与实现 系统介绍 浏览记录系统主要用来记录京东用户的实时浏览记录&#xff0c;并提供实时查询浏览数据的功能。在线用户访问一次商品详情页&#xff0c;浏览记录系统就会记录用户的一条浏览数据&#xff0c;并针对该浏览数据进行商品维度…

分享80个行业PPT,总有一款适合您

分享80个行业PPT&#xff0c;总有一款适合您 80个行业PPT下载链接&#xff1a;https://pan.baidu.com/s/1sc44xGll2RZ8Q2aiKDbjEA?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不易。知…

阿里云ECS服务器Linux安装Mysql8

链接&#xff1a;https://pan.baidu.com/s/1s9j7OhiOMV9e9Qq9GDbysA 提取码&#xff1a;dd5a --来自百度网盘超级会员V5的分享 Mysql官网:MySQL 关于Mysql Yum Repository介绍可以看下 更加简单 关于X86和ARM 传到服务器 进入所在包 cd /usr/local/develop/mysql8 解压 …

数据结构 - 线段树

1. 预制值&#xff1a; 构建的数组为&#xff0c;nums&#xff1a;【2&#xff0c; 5&#xff0c; 1&#xff0c; 4&#xff0c; 3】区间和问题&#xff0c;假设求区间 [1&#xff0c;3] 的和 2. 建树 2.1 构建线段树数组 int[] segT new int[4*n]&#xff08;为什么数组大…

红队打靶练习:PHOTOGRAPHER: 1

目录 信息收集 1、arp 2、nmap 3、nikto 目录扫描 1、gobuster 2、dirsearch WEB 信息收集 enum4linux smbclient 8000端口 CMS利用 信息收集 文件上传漏洞利用 提权 信息收集 get user.txt get flag 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# a…

Codeforces Round 923 (Div. 3)

Codeforces Round 923 (Div. 3) Codeforces Round 923 (Div. 3) A. Make it White 题意&#xff1a;略 思路&#xff1a;找最小和最大的‘B’下标即可 AC code&#xff1a; void solve() {cin >>n;string s; cin>> s;int mn INF, mx 0;for (int i 0; i <…

Linux文件和目录管理

目录基础 Linux操作系统以目录的方式来组织和管理系统中的所有文件。所谓的目录&#xff0c;就是将所有文件的说明信息采用树状结构组织起来。每个目录节点之下会有文件和子目录。 所有一切都从 ‘根’ 开始&#xff0c;用 ‘/’ 代表, 并且延伸到子目录。 bin&#xff1a;B…

ongoDB从入门到实战之.NET Core使用MongoDB开发ToDoList系统(2)-Swagger框架集成

Swagger是什么&#xff1f; Swagger是一个规范且完整API文档管理框架&#xff0c;可以用于生成、描述和调用可视化的RESTful风格的 Web 服务。Swagger 的目标是对 REST API 定义一个标准且和语言无关的接口&#xff0c;可以让人和计算机拥有无须访问源码、文档或网络流量监测就…

Java并发基础:Deque接口和Queue接口的区别?

核心概念 Deque&#xff08;double ended queue&#xff0c;双端队列&#xff09;和Queue&#xff08;队列&#xff09;都是Java集合框架中的接口&#xff0c;它们用于处理元素的排队和出队&#xff0c;但是它们之间存在一些重要的区别&#xff0c;如下&#xff1a; 1、Queue…