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';
    else
        std::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();
            else
                g(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 : 0xf
    std::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.mfbz.cn/a/377956.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

我的docker随笔43:问答平台answer部署

本文介绍开源问答社区平台Answer的容器化部署。 起因 笔者一直想搭建一个类似stack overflower这样的平台&#xff0c;自使用了Typora&#xff0c;就正式全面用MarkdownTyporagit来积累自己的个人知识库&#xff0c;但没有做到web化&#xff0c;现在也还在探索更好的方法。 无…

Spring Boot3整合Redis

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途。 目录 前置条件 1.导依赖 2.配置连接信息以及连接池参数 3.配置序列化方式 4.编写测试 前置条件 已经初始化好一个spr…

RisingWave 中文用户文档上线,阅读更高效!

为满足广大中文社区用户、开发者及流处理技术爱好者的需求&#xff0c;RisingWave 用户文档中文社区版今天上线了&#xff01; 中文版文档的推出&#xff0c;旨在为广大用户提供更便捷、高效的阅读体验&#xff0c;帮助大家深入理解并有效使用 RisingWave&#xff0c;发挥其更…

零基础学Python之整合MySQL

Python 标准数据库接口为 Python DB-API&#xff0c;Python DB-API为开发人员提供了数据库应用编程接口。 不同的数据库你需要下载不同的DB API模块&#xff0c;例如你需要访问Oracle数据库和Mysql数据&#xff0c;你需要下载Oracle和MySQL数据库模块。 DB-API 是一个规范. 它…

2023年12月 Python(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,共50分) 第1题 一个非零的二进制正整数,在其末尾添加两个“0”,则该新数将是原数的?( ) A:10倍 B:2倍 C:4倍 D:8倍 答案:C 二进制进位规则是逢二进一,因此末尾添加一个0,是扩大2倍,添加两个0…

【排序】希尔排序

算法图解 算法基本步骤 首先&#xff0c;希尔排序是基于插入排序的一个时间复杂度为O(N*logN)的一个很牛的排序。 大家应该能注意到&#xff0c;图解中每一趟排序的时候有的数背景颜色是一样的&#xff0c;像这样背景颜色相同的数为一组&#xff0c;我们一共可以分gap组。 那…

Google DeepMind最新研究,将视觉语言大模型作为强化学习的全新奖励来源

论文题目&#xff1a;Vision-Language Models as a Source of Rewards 论文链接&#xff1a;https://arxiv.org/abs/2312.09187 在大型语言模型&#xff08;LLM&#xff09;不断发展的进程中&#xff0c;强化学习扮演了重要的角色&#xff0c;ChatGPT就是在GPT-3.5的基础上经过…

创建一个VUE项目(vue2和vue3)

背景&#xff1a;电脑已经安装完vue2和vue3环境 一台Mac同时安装vue2和vue3 https://blog.csdn.net/c103363/article/details/136059783 创建vue2项目 vue init webpack "项目名称"创建vue3项目 vue create "项目名称"

C++初阶:容器(Containers)vector常用接口详解

介绍完了string类的相关内容后&#xff1a;C初阶&#xff1a;适合新手的手撕string类&#xff08;模拟实现string类&#xff09; 接下来进入新的篇章&#xff0c;容器vector介绍&#xff1a; 文章目录 1.vector的初步介绍2.vector的定义&#xff08;constructor&#xff09;3.v…

redis特点

一、redis线程模型有哪些&#xff0c;单线程为什么快&#xff1f; 1、IO模型维度的特征 IO模型使用了多路复用器&#xff0c;在linux系统中使用的是EPOLL 类似netty的BOSS,WORKER使用一个EventLoopGroup(threads1) 单线程的Reactor模型&#xff0c;每次循环取socket中的命令…

【Spring】Tomcat服务器部署

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;Spring⛺️稳中求进&#xff0c;晒太阳 单体项目部署 本地工作 项目在本地开发完毕之后进行一些必要参数的修改。 比如&#xff1a; 数据库的JDBC的配置文件&#xff0c;还有前端页面的…

微软.NET6开发的C#特性——接口和属性

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;看到不少初学者在学习编程语言的过程中如此的痛苦&#xff0c;我决定做点什么&#xff0c;下面我就重点讲讲微软.NET6开发人员需要知道的C#特性。 C#经历了多年发展&#xff0c; 进行了多次重大创新&#xf…

PCIE Order Set

1 Training Sequence Training Sequence是由Order Set(OS) 组成&#xff0c;它们主要是用于bit aligment&#xff0c;symbol aligment&#xff0c;交换物理层的参数。当data_rate 2.5GT or 5GT 它们不会被扰码(scramble)&#xff0c;当date_rate 8GT or higher 根据特殊的规则…

jsp康养小镇管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP康养小镇管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&a…

年货大数据(电商平台年货节数据):水果销售额增长72%,海鲜肉类涨幅高于蔬菜

春节临近&#xff0c;生鲜又成了线上线下“叫卖”狠&#xff0c;竞争大&#xff0c;盈利好的行业之一。无论是线下商超&#xff0c;还是线上电商&#xff0c;生鲜行业在年货节期间不愁没有市场需求。 根据鲸参谋数据显示&#xff0c;1月前三周京东平台生鲜市场整体销量超3300万…

分享一下 uniapp 打包安卓apk

首先需要安装 Java 环境&#xff0c;这里就不做解释了 第二步&#xff1a;打开 mac 终端 / cmd 命令行工具 使用keytool -genkey命令生成证书 keytool -genkey -alias testalias -keyalg RSA -keysize 2048 -validity 36500 -keystore test.keystore *testalias 是证书别名&am…

Spark安装(Yarn模式)

一、解压 链接&#xff1a;https://pan.baidu.com/s/1O8u1SEuLOQv2Yietea_Uxg 提取码&#xff1a;mb4h tar -zxvf /opt/software/spark-3.0.3-bin-hadoop3.2.tgz -C /opt/module/spark-yarn mv spark-3.0.3-bin-hadoop3.2/ spark-yarn 二、配置环境变量 vim /etc/profile…

【华为 ICT HCIA eNSP 习题汇总】——题目集14

1、以下哪种攻击不属于网络层攻击&#xff1f; A、IP 欺骗攻击 B、Smurf 攻击 C、ARP 欺骗攻击 D、ICMP 攻击 考点&#xff1a;网络安全 解析&#xff1a;&#xff08;C&#xff09; IP 欺骗攻击是通过伪造源 IP 地址&#xff0c;冒充计算机与服务器进行通信&#xff0c;从而达…

MIT6.1810/Fall 2022(which was called 6.S081 then) Lab8-10

Lab: locks Memory allocator 程序user/kalloctest强调xv6的内存分配器:三个进程增加和缩小它们的地址空间&#xff0c;导致对kalloc和kfree的多次调用。Kalloc和kfree获取kmem.lock。对于kmem锁和其他一些锁&#xff0c;Kalloctest打印(作为“#test-and-set”)由于试图获取另…

Redis主从复制原理工作流程和常见问题

Redis主从复制原理 相信很多小伙伴都已经配置过主从复制&#xff0c;但是对于redis主从复制的工作流程和常见问题很多都没有深入的了解。咔咔这次用时俩天时间给大家整理一份redis主从复制的全部知识点。本文实现所需环境 centos7.0 redis4.0 一、什么是Redis主从复制&#x…
最新文章