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,一经查实,立即删除!

相关文章

【前端高频面试题--Vue路由篇】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;前端高频面试题 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac;前端高频面试题--Vue路由篇 对Vue-Router的理解Vue路由懒加载的实现路由的hash和history模式如何获…

【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基础开始通过知识和代码实践结合的方式进行知识的分享和记…

数字图像处理与Python语言实现-常见图像特效(二)

文章目录 9、Splash滤镜10、双色调(Duo-Tone)滤镜11、日光(Daylight)滤镜12、60sTVs效果13、高对比度14、棕褐色/复古滤镜15、晕影效果16、模糊滤镜17、浮雕边缘9、Splash滤镜 在Splash滤镜中,仅某些颜色保持原样,其余颜色转换为灰度。 为了执行此操作,我们将在 HSV 颜…

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

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

Codeforces Round 345 (Div. 1)A. Watchmen(容斥原理)

A. Watchmen 当欧几里得距离和曼哈顿距离相等时&#xff0c; x 1 x 2 ∣ ∣ y 1 y 2 x1x2||y1y2 x1x2∣∣y1y2 这两个条件满足其一。这和容斥原理一样&#xff0c;至少选择一个的条件。 我们可以计算xi&#xff0c;以及小于i之前的这些&#xff0c;这样可以保证只计算一次…

学习 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;而是将图像区域分解并表示…

nginx stream proxy 模块的ssl连接源码分析

目录 1. 源起2. 分析验证环境的配置3. 源码分析3.1 代理模块的请求入口点分析3.2 发起与上游服务器的连接3.3 连接回调3.4 TCP连接建立成功后为上下游数据透传做准备3.5 TCP连接的ssl上下文初始化3.6 ssl握手成功后的处理3.7 连接数据的收与发1. 源起 我一直来对ssl建立连接的过…

图片转二进制文件

将图片转换为二进制文件的步骤如下&#xff1a; 打开图片文件&#xff1a;使用适当的编程语言和文件操作函数&#xff0c;如c中的std::ifstream或python中的open函数&#xff0c;打开要转换的图片文件 读取图片数据&#xff1a;使用文件操作函数从打开的图片文件中读取数据。可…

C语言 可变参数列表及相关宏

本文章介绍一下C语言中一些跟可变参数相关的宏及其用法 这里写目录标题 引言va_listva_startva_argva_end以上函数的联合使用样例&#xff1a;vsnprintf__VA_ARGS_ 引言 C语言中有很多的带有可变参数的函数&#xff0c;例如printf函数&#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工具…

Vue中的 v-if 与 v-show 的区别

概述 在 Vue.js 中&#xff0c;我们经常需要根据某些条件来显示或隐藏某些元素。这时&#xff0c;v-if 和 v-show 这两个指令就派上了用场。虽然它们都可以用来控制元素的显示与隐藏&#xff0c;但它们的实现方式、性能影响和适用场景都有所不同。 一、区别比较 1.实现方式 …

飞天使-linux操作的一些技巧与知识点7-acme申请证书

文章目录 申请一个谷歌账号 申请一个谷歌账号 申请账号略 1.进入gcloud的shell 界面 $ gcloud config set project usfesfeef $ gcloud beta publicca external-account-keys create Created an external account key [b64MacKey: 8_QhVdsfefesfesfesf6fnw770bCyfp7vz5FeCUkOo…

Snipaste使用

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

【大厂AI课学习笔记】1.5 AI技术领域(5)图像分类

CV的重要应用场景之一&#xff0c;就是图像分类。 图像分类是根据不同的语义信息区分图像&#xff0c;图像作为输入&#xff0c;分为不同的类。主要使用的方法是深度学习和神经网络。 粗粒度的图像分类&#xff0c;是比较容易的&#xff0c;哪怕是区分猫和老虎&#xff0c;但…

<网络安全>《19 安全态势感知与管理平台》

1 概念 安全态势感知与管理平台融合大数据和机器学习技术&#xff0c;提供可落地的安全保障能力&#xff0c;集安全可视化、监测、预警和响应处置于一体。它集中收集并存储客户I环境的资产、运行状态、漏洞、安全配置、日志、流量等安全相关数据&#xff0c;内置大数据存储和多…

Go基础知识学习-习题题解

Exercise: Loops and Functions&#xff0c;学习for循环 package mainimport ("fmt" )func Sqrt(x float64) float64 {z:1.0for i:0;i<10;i{z - (z*z - x) / (2*z)fmt.Println(z)}return z }func main() {fmt.Println(Sqrt(2)) }Exercise: Slices package mainim…