(C++17) any的使用与简单实现

文章目录

  • 需求
  • 使用
    • ref示例
    • 构造
      • std::make_any
      • emplace
    • 访问
      • std::any_cast<>
        • 普通对象转换
        • 指针转换
        • 引用转换
    • 逻辑判断
  • ⭐手动实现
  • END

需求

在C++这种静态强类型语言中,在一些开发场景下有时需要一种万能类型。

这之后std::any就应运而生了,any 描述用于任何类型的单个值的类型安全容器。

使用

std::any - cppreference.com

注意std::any不是一个模板类,但内部实现基本都是模板

ref示例

其实这个示例已经将大多数功能都有所演示了。

#include <any>
#include <iostream>

int main() {
    std::cout << std::boolalpha;

    // any 类型
    std::any a = 1;
    std::cout << a.type().name() << ": " << std::any_cast<int>(a) << '\n';
    a = 3.14;
    std::cout << a.type().name() << ": " << std::any_cast<double>(a) << '\n';
    a = true;
    std::cout << a.type().name() << ": " << std::any_cast<bool>(a) << '\n';

    // 有误的转型
    try {
        a = 1;
        std::cout << std::any_cast<float>(a) << '\n';
    } catch (const std::bad_any_cast& e) {
        std::cout << e.what() << '\n';
    }

    // 拥有值
    a = 1;
    if (a.has_value()) {
        std::cout << a.type().name() << '\n';
    }

    // 重置
    a.reset();
    if (!a.has_value()) {
        std::cout << "no value\n";
    }

    // 指向所含数据的指针
    a      = 1;
    int* i = std::any_cast<int>(&a);
    std::cout << *i << "\n";
}

构造

std::make_any

与大多数make工程函数一样

#include <any>
#include <iostream>
#include <string>

int main() {
    auto anyy = std::make_any<std::string>(3, 'a');
    std::cout << anyy.type().name() << std::endl;
    std::cout << std::any_cast<std::string>(anyy) << std::endl;
}

emplace

#include <any>
#include <iostream>
#include <string>

int main() {
    std::any anyy = std::string("123");
    std::cout << std::any_cast<std::string>(anyy) << std::endl;
    
    anyy.emplace<std::string>(3, 'a');
    std::cout << std::any_cast<std::string>(anyy) << std::endl;
}

访问

std::any_cast<>

std::any_cast<>是很大的一个讲究。因为涉及各种数据类型问题。

普通对象转换

当检测到数据类型错误时,会抛出异常bad any_cast

#include <any>
#include <iostream>
#include <string>

int main() {
    auto a = std::any(12);
    std::cout << std::any_cast<int>(a) << '\n';

    try {
        std::cout << std::any_cast<std::string>(a) << '\n';
    } catch (const std::bad_any_cast& e) {
        // bad any_cast
        std::cout << e.what() << '\n';
    }
}
指针转换
#include <any>
#include <iostream>
#include <string>
#include <utility>

void show(std::any& a) {
    // 指针示例
    // 转换失败为 nullptr
    // 成功为 内部实际存储对象地址
    if (int* i = std::any_cast<int>(&a)) {
        std::cout << "ptr = " << i << " val = " << *i << std::endl;
    } else if (std::string* s = std::any_cast<std::string>(&a)) {
        std::cout << "ptr = " << s << " val = " << *s << std::endl;
    } else {
        std::cout << "The anyObj is not a int or string\n";
    }
}

int main() {
    // 简单示例
    auto a = std::any(12);
    std::cout << "ptr = " << &a << std::endl;
    show(a);

    a.emplace<std::string>(std::string("Hello World"));
    std::cout << "ptr = " << &a << std::endl;
    show(a);
}

某次的测试结果是这样的。

可见内是对对象进行了重新的构造。

ptr = 0x61fe98
ptr = 0x61fe9c val = 12
ptr = 0x61fe98
ptr = 0x8f7a20 val = Hello World
引用转换

自行注意各种应用在使用的时候的区别。

注意转为右值引用时候注意所有权的问题。

#include <any>
#include <iostream>
#include <string>

int main() {
    auto a = std::any(std::string("hello"));
    std::cout << std::any_cast<std::string>(a) << std::endl;

    // T&
    auto& ref = std::any_cast<std::string&>(a);
    ref[0]    = 'H';
    std::cout << std::any_cast<std::string>(a) << std::endl;

    // const T&
    const auto& cref = std::any_cast<const std::string&>(a);
    // error
    // cref[0] = 'W';

    // T&&
    // 这里加不加 std::move() 效果都一样
    auto&& b = std::any_cast<std::string&&>(std::move(a));
    std::cout << "ptr = " << &b << " val = " << b << std::endl;
    std::cout << "ptr = " << std::any_cast<std::string>(&a)
              << " val = " << *std::any_cast<std::string>(&a) << std::endl;
}

逻辑判断

因为有没有operator bool()所以不可以直接在if等逻辑判断中使用。

#include <any>

int main() {
    // 简单示例
    auto a = std::any(12);
    std::cout << std::any_cast<int>(a) << '\n';

    if (a.has_value()) {
    }
    // 没有 operator bool()
    // if (a) {}
}

⭐手动实现

借助模板技术,我们可以手动实现一个Any。

首先很使用智能指针可以帮我们减少很多对内存问题的负担。

然后主要是能够做到不同数据类型的切换,普通的方法肯定是不行的。

好在C++中有模板类的技术,通过继承的方式,达到类型擦除的效果。

#include <iostream>
#include <memory>

class Any {
private:
    template <typename _Ptr>
    using SmartPtr = std::unique_ptr<_Ptr>;

private:  // 通过继承的方式达到类型擦除
    struct Value {
        virtual SmartPtr<Value> clone() = 0;
        virtual ~Value() {
        }
    };

    template <typename T>
    struct ValueHolder : Value {
        T data;
        ValueHolder(T t) : data(t) {
        }

        /**
         * !!! 注意,这里使用 std::unique_ptr的话
         * 不能直接`return new ValueHolder<T>(data)`
         * 自己手写的智能指针,没有这个问题
         * =======================================
         * error: could not convert
         * '(operator new(8), (<statement>,
         * ((Any::ValueHolder<int>*)<anonymous>)))' from
         * 'Any::ValueHolder<int>*'
         * to
         * 'Any::SmartPtr<Any::Value> {
         *      aka std::unique_ptr<Any::Value, std::default_delete<Any::Value>
         * >
         * }'
         */
        virtual SmartPtr<Value> clone() override {
            // return new ValueHolder<T>(data)
            return SmartPtr<Value>{new ValueHolder<T>(data)};
        }
    };

private:
    SmartPtr<Value> m_value;

public:
    template <typename T>
    Any(T t) : m_value{new ValueHolder<T>(t)} {
    }

    Any(const Any &rhs) {
        this->operator=(rhs);
    }

    Any &operator=(const Any &rhs) {
        m_value = rhs.m_value->clone();
        return *this;
    }

public:
    // 这里是否返回引用,均可(看调用的最终目的)
    template <typename T>
    T &value() const {
        // 查看是否能够进行类型转换
        if (auto p = dynamic_cast<ValueHolder<T> *>(m_value.get())) {
            return p->data;
        }
        // 类型转换失败则抛出异常
        throw std::logic_error("value() => Type Error");
    }
};

/**
 * 检测传参的拷贝操作
 */
void show(Any a) {
}

int main() {
    Any anyy = 1;
    show(anyy);

    try {
        anyy.value<int>() += 1;
        int inter = anyy.value<int>();
        std::cout << typeid(inter).name() << " " << inter << std::endl;

        anyy     = "abc";
        // 这里存的是 const char*
        auto str = anyy.value<std::string>();
        std::cout << typeid(str).name() << " " << str << std::endl;
    } catch (const std::exception &e) {
        std::cerr << e.what() << '\n';
    }
}

参考:

【C++ STL】C++17 实用 std::any 存储任意类型_哔哩哔哩_bilibili

std::any 详细使用方法 - 哔哩哔哩 (bilibili.com)




END

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

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

相关文章

web3 前端dapp从redux过滤出 (我创建与别人创建)正在执行的订单 并展示在Table上

上文 web3 从redux中拿出所有已完成订单 并渲染到对应的Table列表中 我们从redux中 取出并渲染了 已完成的订单 那么 我们继续 万里长征 就快看到尽头了呀 我们先起一下环境 ganache 终端输入 ganache -d然后 登一下 MetaMask 然后 打开我们的项目 发布一下合约 truffle mig…

GZ038 物联网应用开发赛题第4套

2023年全国职业院校技能大赛 高职组 物联网应用开发 任 务 书 &#xff08;第4套卷&#xff09; 工位号&#xff1a;______________ 第一部分 竞赛须知 一、竞赛要求 1、正确使用工具&#xff0c;操作安全规范&#xff1b; 2、竞赛过程中如有异议&#xff0c;可向现场考评…

SAP 50策略测试简介

上篇博文写了40策略的测试,40策略就是典型的按库存生产,考虑库存,考虑销售订单。 本文将测试50策略,按单生产用的最多的策略。相信很多公司按单生产应该都会用到50的策略 1、首先还是先创建物料AB3 同时将BOM中的原材料的独立集中的字段设置为1 2、创建BOM—CS01 3、同杨…

动态通讯录及程序保存在文件中

目录 一、结构体改造及增容函数 1.结构体部分 2.初始化函数及增容函数 二、信息添加及销毁和排序 1.信息添加函数&#xff08;Add&#xff09; 2.销毁函数&#xff08;Destroy&#xff09; 3.排序部分&#xff08;qsort&#xff09; 三、通讯录信息保存 1.保存在文件中…

【华为数通HCIP | 网络工程师】821-BGP 组播高频题与解析(1)

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

在线直线度测量仪为什么在轧钢行业越来越受欢迎!

在线直线度测量仪是利用光电检测原理及直线法进行直线度尺寸精密检测的。其测量方法是前后两台测量仪测量的数据拟合一条直线&#xff0c;中间的测量仪所测数值与直径做对比&#xff0c;即可得到被测物的直线度尺寸。 在线直线度测量仪的优点 在线直线度测量仪是一种三台小测…

Vue生命周期全解析:从工厂岗位到任务执行,一览无遗!

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、生…

Bean的循环依赖问题

2023.11.10 通俗来讲&#xff0c;循环依赖指的是一个实例或多个实例存在相互依赖的关系&#xff08;类之间循环嵌套引用&#xff09;。比如&#xff1a;丈夫类Husband&#xff0c;妻子类Wife。Husband中有Wife的引用。Wife中有Husband的引用。 正常调用这两对象不会出现问题&am…

python实现全向轮EKF_SLAM

python实现全向轮EKF_SLAM 代码地址及效果运动预测观测修正参考算法 代码地址及效果 代码地址 运动预测 简化控制量 u t u_t ut​ 分别定义为 v x Δ t v_x \Delta t vx​Δt&#xff0c; v y Δ t v_y \Delta t vy​Δt&#xff0c;和 ω z Δ t \omega_z \Delta t ωz…

解压游戏资源,导出游戏模型

游戏中有很多好看的角色&#xff0c;地图等等资源。 你有没有想过&#xff0c;把他们导出到自己的游戏中进行魔改又或则玩换肤等操作呢&#xff1f; 相信很多同学都喜欢拳皇中的角色&#xff0c; 那么我们今天就拿拳皇15举例子&#xff0c;导出他的资源。 首先要先安装好这个…

通过商品ID获取到京东商品详情页面数据,京东商品详情官方开放平台API接口,京东APP详情接口,可以拿到sku价格,销售价演示案例

淘宝SKU详情接口是指&#xff0c;获取指定商品的SKU的详细信息。SKU是指提供不同的商品参数组合的一个机制&#xff0c;通过不同的SKU来标识商品的不同组合形式&#xff0c;如颜色、尺寸等。SKU详情接口可以帮助开发者获取指定商品的SKU列表&#xff0c;以及每个SKU的属性、库存…

算法:穷举,暴搜,深搜,回溯,剪枝

文章目录 算法基本思路例题全排列子集全排列II电话号码和字母组合括号生成组合目标和组合总和优美的排列N皇后有效的数独解数独单词搜索黄金矿工不同路径III 总结 算法基本思路 穷举–枚举 画出决策树设计代码 在设计代码的过程中&#xff0c;重点要关心到全局变量&#xff…

ChatGPT风潮再起!最新国内产品一网打尽,畅游指南曝光!

一、国内类chatgpt产品 在人工智能领域&#xff0c;自然语言处理&#xff08;NLP&#xff09;是一个重要的方向&#xff0c;涉及到语音识别、文本生成、机器翻译、问答系统等多个应用场景。近年来&#xff0c;随着深度学习技术的发展&#xff0c;NLP也取得了突破性的进展&#…

React向组件内部动态传入带内容的结构--props

children props&#xff1a;通过组件标签体传入结构 <A><B>xxx</B> </A> {this.props.children}render props&#xff1a;通过组件标签属性传入结构&#xff0c;一般用render函数属性 <A render{data> <C data{data}></C>}></…

super() 和 super(props) 有什么区别?

一、ES6 类 在 ES6 中&#xff0c;通过 extends 关键字实现类的继承&#xff0c;方式如下&#xff1a; class sup { constructor(name) { this.name name; } printName() { console.log(this.name); }}class sub extends sup { constructor(name, age) { …

二十二、W5100S/W5500+RP2040树莓派Pico<SMTP发送邮件>

文章目录 1 前言2 简介2 .1 什么是SMTP&#xff1f;2.2 SMTP是如何工作的&#xff1f;2.3 SMTP、IMAP和POP32.4 SMTP应用场景 3 WIZnet以太网芯片4 SMTP发送邮件示例概述以及使用4.1 流程图4.2 准备工作核心4.3 连接方式4.4 主要代码概述4.5 结果演示 5 注意事项6 相关链接 1 前…

常见产品结构四大类型 优劣势比较

一般&#xff0c;我们通过产品架构来构建用户体验&#xff0c;这样可以提供更清晰的导航和组织、优化用户流程和交互、增强产品的可扩展性和可维护性&#xff0c;提升用户的满意度和忠诚度。如果没有明确的产品结构&#xff0c;可能会导致功能冗余或功能缺失、交互流程混乱等问…

「Verilog学习笔记」使用generate…for语句简化代码

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 分析 generate…for语句是Verilog HDL语言特有的语句&#xff0c;使用循环结构编写可综合的多个形式相近的代码&#xff0c;循环变量必须由特定关键字genvar声明。 timesca…

VSD Viewer v6.16.1(Visio绘图文件阅读器)

VSD Viewer是一款可以打开和查看Microsoft Visio文件的工具&#xff0c;适用于Windows和macOS操作系统。它具有以下优点&#xff1a; 直观易用&#xff1a;VSD Viewer的用户界面非常简单直观&#xff0c;易于使用。支持多种文件格式&#xff1a;VSD Viewer支持多种Visio文件格…

二维码解码器怎么用?快速分解二维码图片的方法

现在很多人会将链接网址生成二维码之后来使用&#xff0c;这种方式能够让别人更快的获取链接的内容&#xff0c;而且扫码访问内容的方式也更适合大家的使用习惯。那么如果想要获取二维码中的链接时&#xff0c;一般会使用二维码解码器来处理&#xff0c;那么具体该怎么使用呢&a…