本文根据LLVM中libcxx的实现,分析了std::any和std::variant的具体实现。

1 简介

  在 C++17 标准中,std::any提供了一种类型安全的方式来存储任意类型的值。它使用类型擦除(type erasure)技术实现,使得一个对象可以包含任何类型的值而不需要提前知道该类型。std::any的使用比较简单。

#include <any>
#include <iostream>int main()
{std::cout << std::boolalpha;// any typestd::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';// bad casttry{a = 1;std::cout << std::any_cast<float>(a) << '\n';}catch (const std::bad_any_cast& e){std::cout << e.what() << '\n';}// has valuea = 2;if (a.has_value())std::cout << a.type().name() << ": " << std::any_cast<int>(a) << '\n';// reseta.reset();if (!a.has_value())std::cout << "no value\n";// pointer to contained dataa = 3;int* i = std::any_cast<int>(&a);std::cout << *i << '\n';
}

2 实现

内存处理
  std::any的实现简单的说来就是通过一个指针存储数据和一个额外的类型信息来保留类型。

class any{union _Storage {_LIBCPP_HIDE_FROM_ABI constexpr _Storage() : __ptr(nullptr) {}void* __ptr;__any_imp::_Buffer __buf;};_HandleFuncPtr __h_ = nullptr;_Storage __s_;
};

  __h_存储不同情况对应处理的函数指针信息,__s_存储具体的内存,根据不同情况使用不同的存储方式。和常规的SSO优化类似小于某个大小的内存直接存储在栈上,否则用堆。这里的大小是对齐到3倍机器字长。

using _Buffer = aligned_storage_t<3 * sizeof(void*), alignof(void*)>;

  不同大小的对象使用不同的handler对数据进行操作,栈内存使用SmallHandle,堆内存使用LargeHandle,二者唯一的区别只是操作的内存不同,一个操作ptr,一个操作buf

template <class _Tp>
struct _LIBCPP_TEMPLATE_VIS _SmallHandler {_LIBCPP_HIDE_FROM_ABI static void*__handle(_Action __act, any const* __this, any* __other, type_info const* __info, const void* __fallback_info) {switch (__act) {case _Action::_Destroy:__destroy(const_cast<any&>(*__this));return nullptr;case _Action::_Copy:__copy(*__this, *__other);return nullptr;case _Action::_Move:__move(const_cast<any&>(*__this), *__other);return nullptr;case _Action::_Get:return __get(const_cast<any&>(*__this), __info, __fallback_info);case _Action::_TypeInfo:return __type_info();}__libcpp_unreachable();}private:_LIBCPP_HIDE_FROM_ABI static void __destroy(any& __this) {typedef allocator<_Tp> _Alloc;typedef allocator_traits<_Alloc> _ATraits;_Alloc __a;_Tp* __p = static_cast<_Tp*>(static_cast<void*>(&__this.__s_.__buf));_ATraits::destroy(__a, __p);__this.__h_ = nullptr;}
};
//而对应的largeHandle的销毁不仅仅要释放对象还需要销毁内存
template <class _Tp>
struct _LIBCPP_TEMPLATE_VIS _LargeHandler {
_LIBCPP_HIDE_FROM_ABI static void __destroy(any& __this) {typedef allocator<_Tp> _Alloc;typedef allocator_traits<_Alloc> _ATraits;_Alloc __a;_Tp* __p = static_cast<_Tp*>(__this.__s_.__ptr);_ATraits::destroy(__a, __p);_ATraits::deallocate(__a, __p, 1);__this.__h_ = nullptr;}
};

  具体使用哪种类型的handle则是在构造时根据类型确定的,_IsSmallObject用来判断是否是小对象,小对象则使用_SmallHandler,否则使用_LargeHandler

template <class _Tp>
using _IsSmallObject =integral_constant<bool,sizeof(_Tp) <= sizeof(_Buffer) && alignof(_Buffer) % alignof(_Tp) == 0 &&is_nothrow_move_constructible<_Tp>::value >;template <class _Tp>
using _Handler = conditional_t< _IsSmallObject<_Tp>::value, _SmallHandler<_Tp>, _LargeHandler<_Tp>>;template <class _ValueType, class _Tp, class>
any::any(_ValueType&& __v) : __h_(nullptr) {__any_imp::_Handler<_Tp>::__create(*this, std::forward<_ValueType>(__v));
}

类型信息
  对于开启了RTTI的场景,比较简单直接返回当前对象的typeinfo即可。

  _LIBCPP_HIDE_FROM_ABI static void* __type_info() {
#  if !defined(_LIBCPP_HAS_NO_RTTI)return const_cast<void*>(static_cast<void const*>(&typeid(_Tp)));
#  elsereturn nullptr;
#  endif}

3 自己实现

  说实话LLVM的实现感觉很丑,这里通过继承来实现类型擦除会好看很多(实现其实不全,拷贝构造等都没有实现,也没有实现类型decay的情况,但是基本功能是OK的)。

#include <iostream>
#include <exception>
#include <type_traits>
#include <memory>
#include <utility>
class AnyCastError : public std::exception {
public:const char* what() const noexcept override {return "Bad cast in Any";}
};template<class T>
class AnyImpl {
public:using Buffer = std::aligned_storage_t<3 * sizeof(void*), alignof(void*)>;using IsSmallTrivialObject = std::integral_constant<bool, sizeof(T) <= sizeof(Buffer)&&alignof(Buffer) % alignof(T) == 0 &&std::is_nothrow_move_constructible<T>::value >;public:union Storage {void* ptr;Buffer buffer;Storage() : ptr(nullptr) {}~Storage() {}};
};struct HolderBase {
public:virtual ~HolderBase() = default;virtual std::unique_ptr<HolderBase> clone() const = 0;virtual const std::type_info *typeInfo() const = 0;
};template<class T>
struct Holder : public HolderBase {
public:Holder(T&& value) {if constexpr (AnyImpl<T>::IsSmallTrivialObject::value) {new(&_storage.buffer)T(std::move(value));}else {_storage.ptr = new T(std::move(value));}}~Holder() {if constexpr (AnyImpl<T>::IsSmallTrivialObject::value) {reinterpret_cast<T*>(&_storage.buffer)->~T();}else {delete static_cast<T*>(_storage.ptr);}}virtual std::unique_ptr<HolderBase> clone() const override {return std::make_unique<Holder<T>>(getValue());}T getValue() const {if constexpr (AnyImpl<T>::IsSmallTrivialObject::value) {return *reinterpret_cast<const T*>(&_storage.buffer);}else {return *static_cast<T*>(_storage.ptr);}}virtual const std::type_info* typeInfo() const override {return &typeid(T);}public:AnyImpl<T>::Storage _storage;
};class Any {
public:Any() {_holder = nullptr;}template<class T>Any(T&& v) {_holder = std::make_unique<Holder<T>>(std::forward<T>(v));}bool hasValue() const {return !!_holder;}template<class T>T getValue() {return hasValue() ? static_cast<Holder<T>*>(_holder.get())->getValue() : T();}const std::type_info* typeInfo() const {return hasValue() ? _holder->typeInfo() : &typeid(int);}
private:std::unique_ptr<HolderBase> _holder{};
};int main(int argc, char **argv){try {Any a = 42; // 存储 intstd::cout << "Value: " << a.getValue<int>() << ", Type: " << a.typeInfo()->name() << std::endl;Any b = std::string("Hello"); // 存储 stringstd::cout << "Value: " << b.getValue<std::string>() << ", Type: " << b.typeInfo()->name() << std::endl;// 测试未存储值的情况Any emptyAny;std::cout << "Has Value: " << emptyAny.hasValue() << std::endl;std::cout << "Type Info: " << emptyAny.typeInfo()->name() << std::endl;}catch (const AnyCastError& e) {std::cerr << e.what() << std::endl;}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/88015.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/88015.shtml
英文地址,请注明出处:http://en.pswp.cn/web/88015.shtml

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

相关文章

网安系列【13】之渗透测试:前期信息收集

文章目录 前期信息收集信息收集的分类信息收集的内容域名信息收集Whois备案信息whois反查SSL证书查询域名收集工具IP收集CDN信息收集CDN判断CDN绕过 端口信息收集常见端口介绍FTP-21SSH-22WWW-80NetBlOSSessionService-139/445MySQL-3306RDP-3389Redis-6379Tomcat-8080 端口扫描…

自动驾驶传感器的标定与数据融合

目录 IMU的标定 相机的标定 激光雷达和组合惯导标定 相机和激光雷达标定 传感器数据融合 多传感器融合数据处理 传感器数据融合算法 环境感知与预测 应用实例——车道线识别 应用实例——车辆行人识别 应用实例——交通标志识别 定位系统 基于惯性导航仪的定位技术…

27.移除元素(快慢指针)

给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k&#xff0c;要通过此题&#xff0c;您需要执行以下操作&#xff1a; 更改…

Spring AI:ETL Pipeline

提取、转换和加载&#xff08;ETL&#xff09;框架是检索增强生成&#xff08;RAG&#xff09;用例中数据处理的支柱。ETL管道协调从原始数据源到结构化向量存储的流程&#xff0c;确保数据以最佳格式供AI模型检索。RAG用例是文本&#xff0c;通过从数据体中检索相关信息来增强…

26.安卓逆向2-frida hook技术-解密响应

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwdzy89 提取码&#xff1…

人工智能与人工智障———仙盟创梦IDE

<!-- 桌面导航 -->&#x3C;nav class&#x22;hidden md:flex items-center space-x-8&#x22;&#x3E;&#x3C;a href&#x22;#home&#x22; class&#x22;nav-link text-gray-700 hover:text-primary font-medium&#x22;&#x3E;&#x9996;&…

车载通信架构 --- 以太网相关网络安全

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…

行业实践案例:金融行业数据治理体系全景解析

“金融行业是数据治理的试金石。” ——高密度数据、高合规要求、高业务依赖,决定了金融治理的复杂度和先进性。 📘 本文目录 为什么金融行业对数据治理要求高? 金融行业数据治理的独特挑战 金融行业治理框架搭建实践 典型治理能力案例详解 工具与平台选型经验 总结与启示 …

C#读取modbus值,C#读写modbus,支持读写uint32值,Modbus TCP工具类

C#读取modbus值&#xff0c;C#读写modbus&#xff0c;支持读写uint32值&#xff1b;Modbus TCP工具类 需要首先安装nuget包Modbus using Modbus.Device; using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; us…

Oracle注释详解

在Oracle SQL中&#xff0c;注释是用于解释代码逻辑、提高可读性的文本&#xff0c;不会被数据库执行。Oracle支持两种类型的注释语法&#xff1a; 1. 单行注释&#xff08;--&#xff09; 使用双连字符--在一行中添加注释&#xff0c;从--开始到行末的所有内容都会被视为注释。…

关于 scrapy框架 详解

scrapy 是一个纯 Python 编写的异步爬虫框架&#xff0c;具备以下特点&#xff1a;优势说明异步高效基于 Twisted&#xff0c;非阻塞 IO模块化各部分可灵活配置/替换中间件机制支持代理、UA、cookie 控制等强大的解析内置 XPath、CSS 提取器自动去重Scheduler 内部维护请求 fin…

DHCP中继实验及其核心原理

DHCP 中继&#xff08;DHCP Relay&#xff09;是一种允许跨网段分配 IP 地址的技术&#xff0c;无需在每个子网部署 DHCP 服务器。以下是其原理和配置方法的详细说明&#xff1a;一、核心原理1. 为什么需要 DHCP 中继&#xff1f;问题&#xff1a;DHCP 客户端通过广播&#xff…

ABP VNext + RediSearch:微服务级全文检索

ABP VNext RediSearch&#xff1a;微服务级全文检索 &#x1f680; &#x1f4da; 目录ABP VNext RediSearch&#xff1a;微服务级全文检索 &#x1f680;&#x1f4da; 一、背景与动机 &#x1f680;&#x1f6e0;️ 二、环境与依赖 &#x1f433;2.1 Docker Compose 启动 R…

TensorFlow深度学习实战——基于自编码器构建句子向量

TensorFlow深度学习实战——基于自编码器构建句子向量 0. 前言1. 句子向量2. 基于自编码器构建句子向量2.1 数据处理2.2 模型构建与训练 3. 模型测试相关链接 0. 前言 在本节中&#xff0c;我们将构建和训练一个基于长短期记忆 (Long Short Term Memory, LSTM) 的自编码器&…

C语言使用Protobuf进行网络通信

笔者前面博文Go语言网络游戏服务器模块化编程介绍了Go语言在开发网络游戏时如何进行模块化编程&#xff0c;在其中使用了Protobuf进行网络通信。在Protobuf官方实现中并没有生成C语言的实现&#xff0c;不过有一个开源的protobuf-c可以使用。 先来看看protobuf-c生成的代码&am…

vue3 随手笔记12--组件通信方式9/5--useAttrs

一 什么是useAttrsuseAttrs 是 Vue 3 Composition API 中提供的一个函数&#xff0c;它属于 Vue 的组合式 API 工具集的一部分。通过 useAttrs&#xff0c;你可以访问传递给组件但未被声明为 props 的所有属性。这对于处理非 prop 特性&#xff08;attributes&#xff09;特别有…

HumanRisk-自动化安全意识与合规教育平台方案

权威数据显示&#xff0c;74%以上的数据泄露与网络安全事件归根结底与人为因素有关&#xff0c;60%以上的网络安全事件是由内部人员失误造成的。这一现状揭示了一个核心命题&#xff1a;网络安全威胁正从技术漏洞转向“人为因素风险”。Gartner的调查发现&#xff0c;即便接受了…

2025年食品科学与健康大数据国际会议(SHBD 2025)

2025年食品科学与健康大数据国际会议 2025 International Conference on Food Science and Health Big Data&#xff08;一&#xff09;大会信息 会议简称&#xff1a;ICFSHBD 2025 大会地点&#xff1a;中国上…

CompareFace人脸识别算法环境部署

一、docker 安装 步骤1&#xff1a;启用系统功能 右键开始菜单 → 应用和功能 → 点击 程序和功能 → 勾选 Hyper-V 和 Windows子系统Linux 步骤2&#xff1a;获取安装包 访问Docker官网安装包下载页 &#xff0c;下载「Docker Desktop Installer.rar」压缩包 步骤3&#…

STM32固件升级设计——内部FLASH模拟U盘升级固件

目录 一、功能描述 1、BootLoader部分&#xff1a; 2、APP部分&#xff1a; 二、BootLoader程序制作 1、分区定义 2、 主函数 3、配置USB 4、配置fatfs文件系统 5、程序跳转 三、APP程序制作 四、工程配置&#xff08;默认KEIL5&#xff09; 五、运行测试 结束语…