Modern C++ std::any为何要求Tp可拷贝构造?

小问题也会影响设计的思路,某个问题或某种case的探讨有助于理解设计的初衷。

声明:以下_Tp/Tp都是指要放入std::any的对象的类型。

它要求_Tp is_copy_constructible, 仅仅是因为有很多函数的实现调用了Tp的拷贝构造函数吗?比如说上节提到的初始化函数:

any(_Tp&& __value) //调用_Tp copy ctor/move ctor
any(in_place_type_t<_Tp>, _Args&&... __args)  //调用_Tp parameterized ctor
any(in_place_type_t<_Tp>, initializer_list<_Up> __il, _Args&&... __args) //调用_Tp parameterized ctoroperator=(_Tp&& __rhs) //先构造临时any对象(调用第一种情况),再move给*this

第一种case:如果__value是左值,则最终会调用到_Tp copy ctor:

_Manager_internal template<typename _Up>static void_S_create(_Storage& __storage, _Up&& __value){void* __addr = &__storage._M_buffer;::new (__addr) _Tp(std::forward<_Up>(__value));}_Manager_external template<typename _Up>static void_S_create(_Storage& __storage, _Up&& __value){__storage._M_ptr = new _Tp(std::forward<_Up>(__value));}

简单写个例子,证实我们的推断:

  1 #include <any>2 #include <iostream>3 using namespace std;45 int main()6 {7     class Person{8         private:9             int _age;10             int _height;11         public:12             Person(){cout<<"default ctor"<<endl;}1314             Person(int age,int h):_age(age),_height(h){15                 cout<<"parameterized ctor"<<endl;16             }17             Person(const Person& o): _age(o._age), _height(o._height){ cout<<"copy ctor"<<endl; }1819             Person& operator=(const Person& o){20                 if (this != &o) {21                     _age = o._age;22                     _height = o._height;23                     cout<<"assignment"<<endl;24                 }25                 return *this;26             }2728             Person(Person&& o) noexcept : _age(std::move(o._age)), _height(std::move(o._height)) {29                 o._age = 0;30                 o._height = 0;31                 std::cout << "move ctor" << std::endl;32             }
3334             Person& operator=(Person&& o) noexcept {35                 if (this != &o) {36                     _age = std::move(o._age);37                     _height = std::move(o._height);38                     o._age = 0;39                     o._height = 0;40                     std::cout << "move assignment" << std::endl;41                 }42                 return *this;43             }4445             void print(){46                 cout<<"age:"<<_age<<" height:"<<_height<<endl;47             }48     };49     any a1{ Person(1,2) }; //call Person's move ctor50     any a2(a1);            //call Person's copy ctor51     cout<<"----------------"<<endl;5253     Person p = Person(1,2);54     any a3(p);             //call Person's copy ctor55     cout<<"----------------"<<endl;5657     any a4(std::in_place_type<Person>, 3, 4); //call Person's parameterized ctor58     Person& p4 = std::any_cast<Person&>(a4);  //ref, no copy of Person59     p4.print();6061     return 0;62 }

输出如下:

parameterized ctor
move ctor
copy ctor
----------------
parameterized ctor
copy ctor
----------------
parameterized ctor
age:3 height:4
 

 50、54行都调用了Person's copy ctor.

50行:any copy

54行:由一个左值Person对象初始化一个any对象

但是不是所有暴露出来的接口都需要Tp支持copy constructible哪?是不是不支持就无法完成初始化+获取回来数据 这种有意义的操作哪?显然不是,请看57-59行。

57行:调用Tp的parameterized ctor构造了一个any对象,实际没调用Tp的copy ctor, 但实现中依然要求Tp是copy constructible的, 如下所示:

    template <typename _Tp, typename... _Args, typename _VTp = decay_t<_Tp>,typename _Mgr = _Manager<_VTp>,__any_constructible_t<_VTp, _Args&&...> = false>explicitany(in_place_type_t<_Tp>, _Args&&... __args): _M_manager(&_Mgr::_S_manage){_Mgr::_S_create(_M_storage, std::forward<_Args>(__args)...);}template <typename _Res, typename _Tp, typename... _Args>using __any_constructible= enable_if<__and_<is_copy_constructible<_Tp>,is_constructible<_Tp, _Args...>>::value,_Res>;template <typename _Tp, typename... _Args>using __any_constructible_t= typename __any_constructible<bool, _Tp, _Args...>::type;

看到enable_if<__and_<is_copy_constructible<_Tp>没?正是这厮!

不相信的话,我们做个试验:把我们上面例子中的第17行改为copy ctor deleted. 删除49~56行

Person(const Person& o)=delete;

编译报错!

 好了,我们修改一下any源代码,把198行注释掉以期盼甩掉对Tp copy ctor的限制:

196     template <typename _Tp, typename... _Args, typename _VTp = decay_t<_Tp>,
197               typename _Mgr = _Manager<_VTp>>
198               //__any_constructible_t<_VTp, _Args&&...> = false>
199       explicit
200       any(in_place_type_t<_Tp>, _Args&&... __args)
201       : _M_manager(&_Mgr::_S_manage)
202       {
203         _Mgr::_S_create(_M_storage, std::forward<_Args>(__args)...);
204       }

编译竟然又报错:

明明调用不到Tp的copy ctor啊, 为何报错???

让我们仔细看下589行:

573     any::_Manager_internal<_Tp>::
574     _S_manage(_Op __which, const any* __any, _Arg* __arg)
575     {
576       // The contained object is in _M_storage._M_buffer
577       auto __ptr = reinterpret_cast<const _Tp*>(&__any->_M_storage._M_buffer);
578       switch (__which)
579       {
580       case _Op_access:
581         __arg->_M_obj = const_cast<_Tp*>(__ptr);
582         break;
583       case _Op_get_type_info:
584 #if __cpp_rtti
585         __arg->_M_typeinfo = &typeid(_Tp);
586 #endif
587         break;
588       case _Op_clone:
589         ::new(&__arg->_M_any->_M_storage._M_buffer) _Tp(*__ptr);

 虽然运行时没用到,但是编译依然要编译这一行,编译::new ... Person(const Person&)肯定报错啊,因为没有这个函数!这下好了,不管你用到没用到Tp的copy ctor, 为了编译通过,你的Tp必须要有copy ctor了,躲不过了!

_Manager_external也一样躲不过:

622       case _Op_clone:
623     __arg->_M_any->_M_storage._M_ptr = new _Tp(*__ptr);
624     __arg->_M_any->_M_manager = __any->_M_manager;

最后,不要忘了把any源代码里的改动恢复回来。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

pikachu之xss获取键盘记录

前备知识 跨域 跨域&#xff08;Cross-Origin&#xff09;是指在互联网中&#xff0c;浏览器为了保护用户信息安全而实施的一种安全策略——同源策略&#xff08;Same-Origin Policy&#xff09;&#xff0c;即浏览器禁止一个域上的文档或者脚本&#xff08;如通过JavaScript发…

Rocky Linux安装部署Elasticsearch(ELK日志服务器)

一、Elasticsearch的简介 Elasticsearch是一个强大的开源搜索和分析引擎&#xff0c;可用于实时处理和查询大量数据。它具有高性能、可扩展性和分布式特性&#xff0c;支持全文搜索、聚合分析、地理空间搜索等功能&#xff0c;是构建实时应用和大规模数据分析平台的首选工具。 …

(十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目

前言 本节内容是一套关于微服务项目在docker环境中使用jenkins流水线部署的完整方案,在开始本节内容之前,我们需要提前安装好docker环境,以及docker本地镜像仓库docker harbor,同时安装好SonarQube用于代码验证,具体的安装步骤可参考作者的往期博客内容。 正文 在源码仓…

【MySQL】学习和总结标量子查询

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-kLo6jykc7AcEVEQk {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

爱普生为姿态和振动控制开发理想的IMU

- M-G370PDS0改善了短期噪声&#xff0c;扩展了型号阵容&#xff0c;使产品选择范围更广- 精工爱普生公司(“爱普生”&#xff0c;TSE: 6724)已经开发出M-G370PDS0样品&#xff0c;这是一种配备高性能六轴传感器的新型惯性测量单元(IMU)1。新产品计划于2022年春季批量生产。 爱…

MarkDown实用技巧:MarkDown中如何实现换行?

MarkDown实用技巧&#xff1a;MarkDown中如何实现换行&#xff1f; &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程 &#x1f448; 希望…

虚拟机安装+固定ip地址

一、下载CentOS 二、安装CentOS 1、打开你的VMware Workstation Pro&#xff0c;并点击“创建新的虚拟机” 2、点选典型(推荐)(T)&#xff0c;并点击“下一步” 3、点选稍后安装操作系统(S)&#xff0c;并点击“下一步” 4、点选Linux&#xff0c;并点击“下一步” 6、点击“…

flutter 一键打出不同包名、应用名、版本名、签名、应用图标、版本号的安装包

1. build.gradle 文件中配置不同的应用信息 flavorDimensions "app"productFlavors {app1 {//配置包名manifestPlaceholders [str: "releaseStr", package_name: "com.example.demo1"]applicationId "com.example.demo1"versionCode …

【SpringCloudAlibaba系列--OpenFeign组件】OpenFeign的配置、使用与测试以及OpenFeign的负载均衡

步骤一 准备两个服务&#xff0c;provider和consumer 本文使用kotlin语言 provider是服务的提供者&#xff0c;由provider连接数据库 RestController RequiredArgsConstructor RequestMapping("/provider/depart") class DepartController(private val departServ…

【GameFramework框架内置模块】6、实体(Entity)

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录&#xff1a; https://blog.csdn.net/q7…

备战蓝桥杯————k个一组反转单链表

k个反转单链表&#xff0c;顾名思义就是k个节点为一组进行反转&#xff0c;这是一道困难的题目&#xff0c;如何解答&#xff0c;可以在我们前面的反转链表中得到思路。 如何 K 个一组反转单链表 题目描述 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xf…

Spring AOP -- 面相切面编程

AOP是Spring框架的核心之一&#xff0c;AOP是一种思想&#xff0c;它的实现方法有很多&#xff0c;有Spring AOP&#xff0c;也有AspectJ、CGLIB等。我们熟知的拦截器其实就是AOP思想的一种实现方式。 AOP是一种思想&#xff0c;是对某一类事情的集中处理。 Spring AOP的实现…

【MySQL】MySQL复合查询--多表查询自连接子查询 - 副本

文章目录 1.基本查询回顾2.多表查询3.自连接4.子查询 4.1单行子查询4.2多行子查询4.3多列子查询4.4在from子句中使用子查询4.5合并查询 4.5.1 union4.5.2 union all 1.基本查询回顾 表的内容如下&#xff1a; mysql> select * from emp; ----------------------------…

IAudioManager.cpp源码解读

IAudioManager.cpp源码如下&#xff1a; 源码路径&#xff1a;https://cs.android.com/android/platform/superproject/main//main:frameworks/native/services/audiomanager/IAudioManager.cpp;drc84410fbd18148d422d3581201c67f1a72a6658c4;l147?hlzh-cn /** Copyright (C)…

JavaWeb——005 请求响应 分层解耦(Postman、三层架构、IOC、DI、注解)

SpringBootWeb请求响应 这里写目录标题 SpringBootWeb请求响应前言1. 请求1.1 Postman1.1.1 介绍1.1.2 安装 1.2 简单参数1.2.1 原始方式1.2.2 SpringBoot方式1.2.3 参数名不一致 1.3 实体参数1.3.1 简单实体对象1.3.2 复杂实体对象 1.4 数组集合参数1.4.1 数组1.4.2 集合 1.5 …

Linux线程(二)----- 线程控制

目录 前言 一、线程资源区 1.1 线程私有资源 1.2 线程共享资源 1.3 原生线程库 二、线程控制接口 2.1 线程创建 2.1.1 创建一批线程 2.2 线程等待 2.3 终止线程 2.4 线程实战 2.5 其他接口 2.5.1 关闭线程 2.5.2 获取线程ID 2.5.3 线程分离 三、深入理解线程 …

WinCC如何与三菱Q系列PLC进行以太网通讯

本文主要描述人机界面WinCC如何与三菱Q系列PLC进行以太网通讯&#xff0c;主要介绍了CPU自带以太网口和扩展以太网模块两种情况以及分别使用TCP、UDP两种协议进行通讯组态步骤及其注意事项。 一、 说明 WinCC从V7.0 SP2版本开始增加了三菱以太网驱动程序&#xff0c;支持和三…

IPD(集成产品开发)—核心思想

企业发展到一定阶段就会遇到管理瓶颈&#xff0c;IPD流程是一种高度结构化的产品开发流程&#xff0c;它集成了业界很多优秀的产品开发方法论&#xff0c;像搭积木一样的组合成一种非常有效的流程。如果我们能根据企业的规模和行业特点&#xff0c;对全流程的IPD进行合适的裁剪…

数字乡村建设全攻略:从0到1的构建思路与实践

数字乡村建设是推进乡村振兴战略、实现农业农村现代化的重要抓手&#xff0c;其目标是通过数字化手段提升乡村治理效能&#xff0c;优化农村公共服务&#xff0c;推动农业产业升级&#xff0c;助力农民增收致富。 以下是从0到1构建数字乡村的总体思路与实践步骤&#xff1a;一、…

Day03:Web架构OSS存储负载均衡CDN加速反向代理WAF防护

目录 WAF CDN OSS 反向代理 负载均衡 思维导图 章节知识点&#xff1a; 应用架构&#xff1a;Web/APP/云应用/三方服务/负载均衡等 安全产品&#xff1a;CDN/WAF/IDS/IPS/蜜罐/防火墙/杀毒等 渗透命令&#xff1a;文件上传下载/端口服务/Shell反弹等 抓包技术&#xff1a…