“vector”: 不是“std”的成员_libcxx 的 std::function 源码分析

链接:functional。其中 std::function 的主体内容在 2100 多行。

先来看 function 的头部。

template<class _Rp, class ..._ArgTypes>
class _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>

其中的 libcpp template vis 宏是用来 controlling symbol visibility 的,我们先不用管。我们先看两个继承,这两个 maybe 类是 traits 类。模板元编程常见的技巧。如果 Rp 和 ArgTypes 满足一元或者二元函数的模板模式(偏特化)的话,那么就继承相应的 unary function 或者 binary function。如果不满足的话就继承空类。其中 unary function 定义是这样。

template <class Arg, class Result>
struct unary_function
{typedef Arg    argument_type;typedef Result result_type;
};

binary 类似。其实就是 typedef 了函数相关类型的空类。

接下来是 std::function 中的数据成员。我们可以看到条件编译。

#ifndef _LIBCPP_ABI_OPTIMIZED_FUNCTIONtypedef __function::__value_func<_Rp(_ArgTypes...)> __func;
#elsetypedef __function::__policy_func<_Rp(_ArgTypes...)> __func;
#endif__func __f_;

其中这个 libcpp abi optimized function 是后加的一个优化选项。可以看到原来用的是 value func 而优化之后用 policy func。具体的 patch 可以见PATCH D55045。两者究竟有什么区别呢?我们瞧瞧。

我们去跟踪 value func。可以看到 value func 的数据成员是这样的。

template <class _Fp> class __value_func;template <class _Rp, class... _ArgTypes> class __value_func<_Rp(_ArgTypes...)>
{typename aligned_storage<3 * sizeof(void*)>::type __buf_;typedef __base<_Rp(_ArgTypes...)> __func;__func* __f_;

我们看到 value func 里面有一个 buf。这个 buf 是 3 个指针长度大小。我们这里假定在 64 位机器吧。那么这个 buf 就是 24 字节。然后还有一个 func 指针指向了一个 base 对象。所以我们得到了一个重要的结论:一个 value func 就是 4 个指针长度,32字节。base 对象是干嘛的?我们跟踪一下。我们可以在代码里看到这样一句话。

// __base provides an abstract interface for copyable functors.

然后是 base 类的定义。就是一个抽象的接口,里面一大堆 = 0 的函数,说是指向一个可拷贝的函子。那这个 buf 又是干嘛的呢?我们继续挖 value func。

这是在 value func 的构造函数代码段节选。

if (__function::__not_null(__f))
{_FunAlloc __af(__a);if (sizeof(_Fun) <= sizeof(__buf_) &&is_nothrow_copy_constructible<_Fp>::value &&is_nothrow_copy_constructible<_FunAlloc>::value){__f_ =::new ((void*)&__buf_) _Fun(_VSTD::move(__f), _Alloc(__af));}else{typedef __allocator_destructor<_FunAlloc> _Dp;unique_ptr<__func, _Dp> __hold(__af.allocate(1), _Dp(__af, 1));::new ((void*)__hold.get()) _Fun(_VSTD::move(__f), _Alloc(__a));__f_ = __hold.release();}
}

可以看到,我们的构造函数根据 sizeof Fun 有了差别。如果 buf 能装得下 Fun(并且拷贝不会抛异常)那么我们的 func 指针直接去指向 buf。并且在 buf 上构造(replacement new)我们的 Fun。如果装不下(或者拷贝抛异常)那么就只能 allocate 出来内存来存放这个 Fun 了。至于这里为什么用 unique ptr 这么迂回的方式,我也不是很懂,但是我猜测和异常有关。所以,我们又得到了一个重要的结论,如果 buf 上可以分配得下 Fun(并且拷贝不抛异常),那么直接在 Buf 上分配(栈)。否则会去 allocate 内存。

关于 value func 就说到这,value func 里面其他的东西都比较正常。其中 value func 的 swap 函数实现的也比较崎岖,感兴趣可以看看。

接下来是 policy func 了。首先我们先看 policy storage。

union __policy_storage
{mutable char __small[sizeof(void*) * 2];void* __large;
};

可以看到这时候用一个 union 节省了内存。并且 small 的 size 变成了 2 个指针长度即 16 个字节。一般的函数指针是不会超过这个数的,但是其他的函子可以很膨胀。这里留一个思考题:为什么一般的函数指针不会超过 16 个字节,而不是 8 个字节?提示:成员函数指针。

这是一个配套 policy storage 使用的 traits 类。

template <typename _Fun>
struct __use_small_storage: public _VSTD::integral_constant<bool, sizeof(_Fun) <= sizeof(__policy_storage) &&_LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&_VSTD::is_trivially_copy_constructible<_Fun>::value &&_VSTD::is_trivially_destructible<_Fun>::value> {};

很显然,如果 Fun 满足条件那么 use small storage 里的 value 是 true,否则是 false。很简单的模板元编程。注意这里的条件没有 allocator 了。因为在 C++17 之后,std::function 就不用 allocator 了。

在看 policy func 之前,我们还得看一个类,invoker。

// __policy_invoker calls an instance of __alloc_func held in __policy_storage.template <class _Fp> struct __policy_invoker;template <class _Rp, class... _ArgTypes>
struct __policy_invoker<_Rp(_ArgTypes...)>
{typedef _Rp (*__Call)(const __policy_storage*,__fast_forward<_ArgTypes>...);__Call __call_;

其中 fast forward 先不用管,是一个传参策略的 traits。我们看到 policy invoker 里面有一个 Call 函数指针。这个函数指针指向的函数接受 policy storage 和函子的参数,然后返回函子的返回值。为什么需要这么一个类呢?我们往下看。

// Creates an invoker that calls the given instance of __func.
template <typename _Fun>
_LIBCPP_INLINE_VISIBILITY static __policy_invoker __create()
{return __policy_invoker(&__call_impl<_Fun>);
}

这里返回了一个 policy invoker 对象,并且用 call impl 初始化了 Call 函数指针。再瞧瞧 call impl。

template <typename _Fun>
static _Rp __call_impl(const __policy_storage* __buf,__fast_forward<_ArgTypes>... __args)
{_Fun* __f = reinterpret_cast<_Fun*>(__use_small_storage<_Fun>::value? &__buf->__small: __buf->__large);return (*__f)(_VSTD::forward<_ArgTypes>(__args)...);
}

可以看到这个 call impl 函数先判定 Fun 存储在哪里,然后就去调用它。

所以,为什么需要 invoker 呢?因为我们这里用的是(内存和指向其他内存的指针)的一个 union。所以取内存方式会不同,中间会差一层取地址的抽象(见源码的 small 和 large 的取法,small 要多一层取地址)。

而 value func 为什么可以统一取内存的方式呢?因为 value func 的指向内存的指针 func 和 buf 是分开的,即使分配到了 buf 上,value func 的 func 指针依然会指向这个 buf。所以无论如何,只要通过这个 func 指针来获取内存,一定就是没错的。没有疑问。

好了,我们可以看 policy func 了。

// __policy_func uses a __policy and __policy_invoker to create a type-erased,
// copyable functor.template <class _Fp> class __policy_func;template <class _Rp, class... _ArgTypes> class __policy_func<_Rp(_ArgTypes...)>
{// Inline storage for small objects.__policy_storage __buf_;// Calls the value stored in __buf_. This could technically be part of// policy, but storing it here eliminates a level of indirection inside// operator().typedef __function::__policy_invoker<_Rp(_ArgTypes...)> __invoker;__invoker __invoker_;// The policy that describes how to move / copy / destroy __buf_. Never// null, even if the function is empty.const __policy* __policy_;

可以看到首先 policy func 里面有 buf,16 个字节。然后是 invoker,8 个字节。最后是 policy 指针,8 个字节。所以加起来还是 32 个字节。其中 policy 类似于 base,也是提供如何进行基本操作的类的指针。

我们再来看看 policy func 的构造函数。

template <class _Fp, class _Alloc>
_LIBCPP_INLINE_VISIBILITY __policy_func(_Fp&& __f, const _Alloc& __a): __policy_(__policy::__create_empty())
{typedef __alloc_func<_Fp, _Alloc, _Rp(_ArgTypes...)> _Fun;typedef allocator_traits<_Alloc> __alloc_traits;typedef typename __rebind_alloc_helper<__alloc_traits, _Fun>::type_FunAlloc;if (__function::__not_null(__f)){__invoker_ = __invoker::template __create<_Fun>();__policy_ = __policy::__create<_Fun>();_FunAlloc __af(__a);if (__use_small_storage<_Fun>()){::new ((void*)&__buf_.__small)_Fun(_VSTD::move(__f), _Alloc(__af));}else{typedef __allocator_destructor<_FunAlloc> _Dp;unique_ptr<_Fun, _Dp> __hold(__af.allocate(1), _Dp(__af, 1));::new ((void*)__hold.get())_Fun(_VSTD::move(__f), _Alloc(__af));__buf_.__large = __hold.release();}}
}

和 value func 差不多。不再赘述了。注意这里的分配内存之后并没有赋值给一个指针,而仅仅是在 buf 上分配内存就完了。获取函子并调用的操作是由 invoker 做的。

我觉得 std::function 的重点就是数据成员这部分。至于相关的成员函数,我觉得都比较正常,这里就不在多说什么了。

那,就这样。

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

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

相关文章

python验证身份证号码大全_身份证号码处理技巧大全

身份证号码处理技巧大全&#xff0c;汇总了常用的身份证号码处理六大技巧&#xff1a;不需要复杂的公式&#xff0c;点点鼠标即可完成&#xff0c;简单快捷&#xff0c;下面将详细介绍六大功能的具体用法。(文章最后有工具和演示文件的下载地址&#xff0c;可以下载下来同步操作…

语言print如何实现连续输出_【每日一题】如何实现一个高效的单向链表逆序输出?...

今后&#xff0c;动力节点Java学院将每天为大家带来一道大厂面试真题&#xff0c;这些面试题都是大厂技术专家们结合多年的工作、面试经验总结提炼而成的面试真题。通过这些面试题&#xff0c;还可以间接地了解技术大牛们出题思路与考察要点。建议大家收藏并分享给更多需要的人…

恩尼格玛模拟器_用C语言编的恩格尼码模拟器

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼void enumio(char pie1[],char pie2[],char pie3[],char pier[],char ch0[],char chz[],char ip[],char k[],int cou){ int check(char *a);int excheck(char *a);int compare(char *le,char *unle);int factorial(int n);void cyc…

louvain算法python_复杂网络任务6:Louvain社区发现算法的原理、细节和实现,作业,六,以及...

ΔQ[∑in2∗mki,in2∗m−(∑tot2∗m)2−(2∗∑tot∗ki4∗m2)−(ki2∗m)2]−[∑in2∗m−(∑tot2∗m)2−(ki2∗m)2]ki,in2∗m−2∗∑tot∗ki4∗m212∗m∗(ki,in−∑tot∗kim)\Delta{Q} [\frac{\sum_{in}} {2*m} \frac{k_{i,in}}{2*m} - (\frac{\sum_{tot}}{2*m})^2 - (\frac{2*…

系统相机裁剪比例_从单反到手机,三种黄金比例构图方法,让你的照片与众不同...

古埃及金字塔和达芬奇蒙娜丽莎有什么共同之处&#xff1f;它们都是使用黄金比例进行设计的。不管是建筑设计还是绘画&#xff0c;它们都是属于艺术的一种&#xff0c;所以黄金比例也同样适用于摄影构图中。很多优秀的摄影作品都会使用黄金比例的构图方法进行拍摄&#xff0c;因…

mysql安装图解_MySQL安装图解

目录一、安装详细过程MySQL默认安装在“C:\Program Files”目录下。普通使用只安装MySQL Server就足够了&#xff0c;大小为416M。如果不想装在C盘&#xff0c;也可以安装完成之后再将其移动到其他盘。1.接受许可&#xff0c;点击Next2.选择安装功能&#xff0c;推荐选择Server…

mysql字符集设置_mysql字符集设置

配置文件路径&#xff1a; /full/path/mysql/bin/my.cnf (默认为/etc/my.cnf )[client]default-character-setutf8[mysql]default-character-setutf8[mysqld]init_connectSET collation_connection utf8_unicode_ciinit_connectSET NAMES utf8character-set-serverutf8collati…

mysql 索引 原理_MySQL——索引实现原理

在MySQL中&#xff0c;索引属于存储引擎级别的概念&#xff0c;不同存储引擎对索引的实现方式是不同的&#xff0c;本文主要讨论MyISAM和InnoDB两个存储引擎的索引实现方式。MyISAM索引实现MyISAM引擎使用BTree作为索引结构。MyISAM会按照数据插入的顺序分配行号&#xff0c;从…

mysql 字段 中文_如何配置mysql支持中文字段名与中文字段

匿名用户1级2018-11-18 回答中文字段名都可以了 但是中文记录不行 奇怪啊mysql>; create table a (a char(20));Query OK, 0 rows affected (0.05 sec)mysql>; insert into a values(^_^);Query OK, 1 row affected (0.05 sec)mysql>; insert into a values(中guo);Qu…

mysql中如何删除多个表格_mysql怎么批量删除多个表?

mysql批量删除多个表的方法&#xff1a;使用“DROP TABLE”语句&#xff0c;只要将表名依次写在后面&#xff0c;相互之间用逗号隔开即可&#xff1b;语法格式“DROP TABLE [IF EXISTS] 表名1 [ ,表名2, 表名3 ...]”。mysql批量删除多个表使用 DROP TABLE 语句可以删除一个或多…

mysql 图片 格式_mysql存储图片 用什么格式

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云数据库专家保驾护航&#xff0c;为用户…

mysql5.5更改端口后初始化_centos7 修改mysql5.7默认端口后启动异常

关闭selinux的方法有两种&#xff1a;临时关闭和永久关闭。查看selinux的状态&#xff1a;sestatus[root162-219-29-3 ~]# sestatusSELinux status: enabledSELinuxfs mount: /sys/fs/selinuxSELinux root directory: /etc/selinuxLoaded policy name: targetedCurrent mode: e…

开启mysql日志记录_Mysql开启日志记录

vim /etc/my.conf.d/server.cnf:#lower_case_tables_name 1#错误日志log_error /var/log/mysql_error.log#慢查询日志slow_query_logONslow_query_log_file/mnt/lnx_log/mysql/slow.loglong_query_time1 #单位秒&#xff0c;超过此值则记录为慢查询#通用查询日志&#xff0c;…

mysql存储引擎innodb_MySQL常用存储引擎之Innodb

1. mysql 5.5及之后版本默认存储引擎为不了解存储引擎的数据库使用者&#xff0c;提供了很大的便利&#xff0c;因为innodb适应大部分应用场景。和myisam不同的是&#xff0c;innodb是一种事务型存储引擎。也就是说&#xff0c;innodb是支持事务的acid特性的。innodb的设计&…

mysql 的驱动是多少_mysql驱动参数变化

在java平台使用的mysql jdbc驱动为&#xff1a;mysql-connector-java。在项目中添加如下依赖&#xff1a;mysqlmysql-connector-java${version.mysql.connector}在6.0.2版本之前drivercom.mysql.jdbc.Driverurljdbc:mysql://host:port/dbname?characterEncodingutf8在6.0.2版本…

mysql 对已有表分区_mysql怎么对先有表进行分区

mysql如何对先有表进行分区&#xff1f;有一张表&#xff0c;已经创建了&#xff0c;是一张普通的表&#xff0c;先要对这张表进行hash分区&#xff0c;我用一下语句创建提示错误&#xff1a;mysql> alter table 33-> partition by hash(id)-> partitions 2;ERROR 106…

如何查看mysql my.ini_MySQL学习笔记(一)

MySQL 学习笔记一.安装Typical:经典安装Custom:自定义安装Complete: 完全安装二.修改编码方式[mysqld]myini:character-set-serverutf8[client] port3306 default-character-setutf8三.启动停止mysql服务cmd net stop mysqlcmd net start mysql四.MySQL登录mysql -V 查看版本&…

springboot quartz 动态配置_springboot集成quartz实现动态任务调度

quartz是一个开源的作业调度框架&#xff0c;本文就是介绍下springboot框架下继承quartz的一些使用示例首先我们需要添加quartz的spring-boot-starter-quartz依赖org.springframework.bootspring-boot-starter-quartz我们需要做一些配置。quartz提供了基于内存(MEMORY)和基于jd…

sql server cdc 清理_基于CDC技术的ElasticSearch索引同步机制

概述ElasticSearch作为一个基于Lucene的搜索引擎被广泛应用于各种应用系统&#xff0c;比如电商、新闻类、咨询类网站。在使用ElasticSearch开发应用的过程中&#xff0c;一个非常重要的过程是将数据导入到ElasticSearch索引中建立文档。在一开始系统规模比较小时&#xff0c;我…

mysql 8.0数据备份恢复_MySQL 8.0 增强逻辑备份恢复工具介绍-爱可生

作者&#xff1a;杨涛涛资深数据库专家&#xff0c;专研 MySQL 十余年。擅长 MySQL、PostgreSQL、MongoDB 等开源数据库相关的备份恢复、SQL 调优、监控运维、高可用架构设计等。目前任职于爱可生&#xff0c;为各大运营商及银行金融企业提供 MySQL 相关技术支持、MySQL 相关课…