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::optional, std::any, std::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
相关文章:

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.访问值…...

2024年:用OKR管理你的生活
在科技高速发展的时代,越来越多的企业和团队开始采用OKR(Objectives and Key Results)管理方法来设定目标并跟踪进度。你是否想过,将OKR理念引入个人生活,以更有效地实现人生目标?本文将探讨如何在2024年运…...

Lua迭代器以及各种源函数的实现
范型for 范型for的格式如下所示: for <var-list> in <exp-list> do<body> end var-list指变量名列表,可以为多个,exp-list指表达式列表,通常情况下只有一个值。可以更具体地写为另一种形式: fo…...

e5 服务器具备哪些性能特点?
随着云计算和大数据技术的不断发展,服务器作为数据中心的核心设备,其性能特点也日益受到关注。其中,E5服务器作为当前主流的服务器类型之一,具备许多优秀的性能特点。本文将详细介绍E5服务器的性能特点,帮助读者更好地…...

《C++ Primer Plus》《2、开始学习C++》
文章目录 0 前言:1 进入C1.1 main()函数1.2 C注释1.3 预处理器和iostream1.4 头文件名1.5 名称空间1.6 使用cout进行C输出1.7 C源代码的格式化 2 C语句2.1 声明语句和变量2.2 赋值语句2.3 cout语句 3 其他C语句3.1使用cin3.2 使用cout进行拼接3.3 类简介 4 函数4.1 …...

Backtrader 文档学习- Sizers
Backtrader 文档学习- Sizers 1.概述 智能仓位 Strategy提供了交易方法,即:buy,sell和close。看一下buy的定义: def buy(self, dataNone,sizeNone, priceNone, plimitNone,exectypeNone, validNone, tradeid0, **kwargs):注意&…...

基于YOLOv8算法的照片角度分类项目实践
目录 一、任务概述二、YOLOv8算法简介2.1 算法改进2.2 算法特点2.3 网络结构2.4 性能比较 三、工程实践3.1 安装算法框架库ultralytics3.2 库存照片预处理3.2.1 提取所有图片3.2.2 去除冗余的相同照片3.2.3 去除无车辆照片3.2.4 随机提取指定数量的图片 3.3 照片朝向分类3.3.1 …...

go语言进阶篇——面向对象(一)
什么是面向对象 在我们设计代码时,比如写一个算法题或者写一个问题结局办法时,我们常常会使用面向过程的方式来书写代码,面向过程主要指的是以解决问题为中心,按照一步步具体的步骤来编写代码或者调用函数,他在问题规…...

C#,栅栏油漆算法(Painting Fence Algorithm)的源代码
1 刷油漆问题 给定一个有n根柱子和k种颜色的围栏,找出油漆围栏的方法,使最多两个相邻的柱子具有相同的颜色。因为答案可以是大的,所以返回10^97的模。 计算结果: 2 栅栏油漆算法的源程序 using System; namespace Legalsoft.Tr…...

java_error_in_pycharm.hprof文件是什么?能删除吗?
java_error_in_pycharm.hprof文件是什么?能删除吗? 🌵文章目录🌵 🌳引言🌳🌳hprof格式文件介绍🌳🌳java_error_in_pycharm.hprof文件什么情况下能删除🌳&…...

LeetCode 491 递增序列
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。 示例: 输入: [4, 6, 7, 7] 输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]说明: 给定数组的长度不会超过15。 数组中的整数范围是 [-…...

考研/计算机二级数据结构刷题之顺序表
目录 第一题 顺序表的初始化,销毁,头插,尾插,头删,尾删,指定位置插入,指定删除以及打印 第二题 移除元素 题目链接: OJ链接 题目详解:移除元素 第三题:删…...

Git 代码协同的使用方法 for Azure DevOps
1. 登陆Azure 步骤1.1. VS Code,登陆Azure Cloud的Ubuntu环境,如下: 重点: 这里的Azure Cloud的用户名是YourAzureUserName,口令是YourAzurePassword 步骤1.2. 登陆Azure Cloud的Ubuntu环境后,配置Git账户信息&…...

数据库学习笔记2024/2/5
2. SQL 全称 Structured Query Language,结构化查询语言。操作关系型数据库的编程语言,定义了 一套操作关系型数据库统一标准 2.1 SQL通用语法 在学习具体的SQL语句之前,先来了解一下SQL语言的通用语法。 1). SQL语句可以单行或多行书写&…...

PSM-Net根据Stereo图像生成depth图像
一、新建文件夹 在KITTI数据集下新建depth_0目录 二、激活anaconda环境 conda activate pt14py37三、修改submission.py文件 3.1 KITTI数据集路径 parser.add_argument(--datapath, default/home/njust/KITTI_DataSet/00/, helpselect model)3.2 深度图像输出路径 save…...

Mocaverse NFT 概览与数据分析
作者:stellafootprint.network 编译:mingfootprint.network 数据源:Mocaverse NFT Collection Dashboard Mocaverse 是 Animoca Brands 推出的专属 NFT(非同质化代币)系列,包含 8,888 个独特的 "M…...

SpringBoot之事务源码解析
首先事务是基于aop的,如果不了解aop的,建议先去看下我关于aop的文章: Spring之aop源码解析 先说结论,带着结论看源码。首先,在bean的生命周期中, 执行实例化前置增强,会加载所有切面并放入缓存࿰…...

FPGA高端项目:解码索尼IMX327 MIPI相机转USB3.0 UVC 输出,提供FPGA开发板+2套工程源码+技术支持
目录 1、前言免责声明 2、相关方案推荐我这里已有的 MIPI 编解码方案 3、本 MIPI CSI-RX IP 介绍4、个人 FPGA高端图像处理开发板简介5、详细设计方案设计原理框图IMX327 及其配置MIPI CSI RX图像 ISP 处理图像缓存UVC 时序USB3.0输出架构FPGA逻辑设计工程源码架构SDK软件工程源…...

基于高通滤波器的ECG信号滤波及心率统计matlab仿真
目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 ECG信号简介 4.2 高通滤波器原理 4.3 心率统计 5.完整工程文件 1.课题概述 通过高通滤波器对ECG信号进行滤波,然后再统计其心率。 2.系统仿真结果 3.核心程序与模型 版本:…...

springCould中的gateway-从小白开始【9】
目录 1.🍟网关是什么 2.🍿gateway是什么 3.🥚gateway能什么 4.🌭核心概念 5.🧂工作流程 6.🧈实例 7.🥓gateway网关配置的方式 8.🍳配置动态路由 9.🧇pred…...

邮箱营销软件推荐?企业邮箱群发会限制吗?
邮箱营销平台怎么选择?哪种邮箱适合做外贸邮件群发? 邮箱营销凭借其精准触达、低成本和高回报的特点,依然是许多企业不可或缺的营销手段。该如何选择一款适合自己的工具呢?蜂邮EDM将为您推荐几款优秀的邮箱营销软件,并…...

编译原理实验3——自下而上的SLR1语法分析实现(包含画DFA转换图、建表、查表)
文章目录 实验目的实现流程定义DFA状态实现代码运行结果测试1测试2测试3 总结 实验目的 实现自下而上的SLR1语法分析,画出DFA图 实现流程 定义DFA状态 class DFA:def __init__(self, id_, item_, next_ids_):self.id_ id_ # 编号self.item_ item_ # productio…...

基于tomcat的https(ssl)双向认证
一、背景介绍 某个供应商服务需要部署到海外,如果海外多个地区需要部署多个服务,最好能实现统一登录,这样可以减轻用户的使用负担(不用记录一堆密码)。由于安全问题(可能会泄露用户数据)&#x…...

【iOS ARKit】3D人体姿态估计实例
与2D人体姿态检测一样,在ARKit 中,我们不必关心底层的人体骨骼关节点检测算法,也不必自己去调用这些算法,在运行使用 ARBodyTrackingConfiguration 配置的 ARSession 之后,基于摄像头图像的3D人体姿态估计任务也会启动…...

ROS2 CMakeLists.txt 和 package.xml
这里记录一下ROS2中功能包package.xml和CMakeLists.txt的格式。以LIO-SAM的ROS2版本为例: 一:CMakeLists.txt cmake_minimum_required(VERSION 3.5) project(lio_sam)if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)set(CMAKE_BUILD_TYPE…...

代码献瑞,算力有礼!低代码开发工具PaddleX特色产线新春福利来啦
回望2023年,飞桨在开发套件能力基础上,充分结合大模型能力,正式在飞桨星河社区上线发布了低代码开发工具PaddleX,实现AI应用开发效果和效率的大幅提升。产品通过提供图形界面开发模式,将复杂的编程任务简化为简单易用的…...

C语言:操作符详解
创作不易,给个三连吧!! 一、算术操作符 C语言中为了方便计算,提供了算数操作符,分别是:,-,*,/,% 由于这些操作符都是有两个操作数(位于操作符两边),所以这种操作符也叫做双目操作…...

Rust 初体验2
变量类型 Rust 语言的变量数据类型,主要包括整型、浮点型、字符、布尔型、元组、数组、字符串、枚举、结构体和可变变量等。 fn main() { // 整型 let integer: i32 100; println!("整型: {}", integer); // 浮点型 let floating_point: f64 3.1…...

vue-cil的watch函数详解
在Vue中,watch是一个非常有用的API,用于侦听一个响应式引用(例如由ref创建)或响应式对象(由reactive创建)的属性,并在值变化时执行回调函数。Vue 3的Composition API引入了这种侦听方式…...

堆排及时间复杂度分析
箴言: 初始阶段,不需要去纠结那一种更优美,非要找出那一种是最好的,其实能解决问题的就是好办法。 一,常见排序时间复杂度 冒泡快排归并堆排桶排时间O(n^2)O(nlogn)O(nlogn)O(nlogn)kn空间O(1)O(1)O(nlogn)O(1)kn 二ÿ…...