c++ 移动构造方法为什么要加noexcept
背景:
最近看了候捷老师的c++的教程, 他说移动构造方法要加noexcept, 在vector扩容的时候, 如果有移动构造方法没有加noexcept,是不会调用的. 个人感觉有些神奇, 这就去查下一探究竟.
过程:
测试代码如下:
#include <iostream>
#include <vector>
struct A
{A(){std::cout<<"A::A()"<<std::endl;}A(const A &a){std::cout<<"A::A(const A&a)"<<std::endl;}A(A &&a) {std::cout<<"A::A(A &&a)"<<std::endl;}A& operator=(const A&a) {std::cout<<"operator=(const A&a)"<<std::endl;return *this;}A& operator = (A &&a){std::cout<<"operator =(A&&a)"<<std::endl;return *this;}
};
int main()
{std::vector<A> vecA;A a;vecA.push_back(a);std::cout<<"1"<<std::endl;vecA.push_back(a);std::cout<<"2"<<std::endl;vecA.push_back(a);std::cout<<"3"<<std::endl;vecA.push_back(a);std::cout<<"4"<<std::endl;return 0;}
执行结果如下:
A::A()
A::A(const A&a)
1
A::A(const A&a)
A::A(const A&a)
2
A::A(const A&a)
A::A(const A&a)
A::A(const A&a)
3
A::A(const A&a)
4
我们知道vector 是要扩容的, 在A(A &&a) 并没有添加noexcept关键字, 所以扩容的时候,使用的也是拷贝构造方法, 那接下来我们看下加下 noexcept 后了,结果是什么样的
#include <iostream>
#include <vector>
struct A
{A(){std::cout<<"A::A()"<<std::endl;}A(const A &a){std::cout<<"A::A(const A&a)"<<std::endl;}A(A &&a) noexcept{std::cout<<"A::A(A &&a)"<<std::endl;}A& operator=(const A&a) noexcept{std::cout<<"operator=(const A&a)"<<std::endl;return *this;}A& operator = (A &&a){std::cout<<"operator =(A&&a)"<<std::endl;return *this;}
};
int main()
{std::vector<A> vecA;A a;vecA.push_back(a);std::cout<<"1"<<std::endl;vecA.push_back(a);std::cout<<"2"<<std::endl;vecA.push_back(a);std::cout<<"3"<<std::endl;vecA.push_back(a);std::cout<<"4"<<std::endl;return 0;}
执行结果如下:
A::A()
A::A(const A&a)
1
A::A(const A&a)
A::A(A &&a)
2
A::A(const A&a)
A::A(A &&a)
A::A(A &&a)
3
A::A(const A&a)
4
在A(A &&a) noexcept 后, 调用的方法就是移动构造方法, 感觉挺不可思议的, 带着这个疑问,我们看下std::vector 源码来找寻答案
揭秘:
push_back 源码如下:
template <class _Tp, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
void
vector<_Tp, _Allocator>::push_back(const_reference __x)
{if (this->__end_ != this->__end_cap()){__RAII_IncreaseAnnotator __annotator(*this);__alloc_traits::construct(this->__alloc(),_VSTD::__to_raw_pointer(this->__end_), __x);__annotator.__done();++this->__end_;}else__push_back_slow_path(__x);
}
因为我们要看扩容相关的代码, __push_back_slow_path(__x); 对应的需要扩容要调用的代码
#ifndef _LIBCPP_CXX03_LANG
vector<_Tp, _Allocator>::__push_back_slow_path(_Up&& __x)
#else
vector<_Tp, _Allocator>::__push_back_slow_path(_Up& __x)
#endif
{allocator_type& __a = this->__alloc();__split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), size(), __a);// __v.push_back(_VSTD::forward<_Up>(__x));__alloc_traits::construct(__a, _VSTD::__to_raw_pointer(__v.__end_), _VSTD::forward<_Up>(__x));__v.__end_++;__swap_out_circular_buffer(__v);
}
上边是分配内从,我们重点看下__swap_out_circular_buffer(__v); 把老的元素拷贝新的申请区域上
template <class _Tp, class _Allocator>
void
vector<_Tp, _Allocator>::__swap_out_circular_buffer(__split_buffer<value_type, allocator_type&>& __v)
{__annotate_delete();__alloc_traits::__construct_backward(this->__alloc(), this->__begin_, this->__end_, __v.__begin_);_VSTD::swap(this->__begin_, __v.__begin_);_VSTD::swap(this->__end_, __v.__end_);_VSTD::swap(this->__end_cap(), __v.__end_cap());__v.__first_ = __v.__begin_;__annotate_new(size());__invalidate_all_iterators();
}
在看下__alloc_traits::__construct_backward 这块 代码
template <class _Ptr>_LIBCPP_INLINE_VISIBILITYstaticvoid__construct_backward(allocator_type& __a, _Ptr __begin1, _Ptr __end1, _Ptr& __end2){while (__end1 != __begin1){construct(__a, _VSTD::__to_raw_pointer(__end2-1), _VSTD::move_if_noexcept(*--__end1));--__end2;}}
代码看到这里,基本已经水落石出了, 我们看到上边有一个很关键的代码_VSTD::move_if_noexcept(*--__end1), 从字面意思也能看出来它是什么意思, 接着看下它的源码
emplate <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11typename conditional
<!is_nothrow_move_constructible<_Tp>::value && is_copy_constructible<_Tp>::value,const _Tp&,_Tp&&
>::typemove_if_noexcept(_Tp& __x) _NOEXCEPT
{return _VSTD::move(__x);
}
这块代码就比较复杂了, move_if_noexcept 返回值使用了SFINA的技术, conditional是一个条件判断语句, 如果它第一类型是true, 则返回const_TP&, 如果是false 则返回类型 _Tp&& , 那就看下!is_nothrow_move_constructible<_Tp>::value && is_copy_constructible<_Tp>::value 这个到底表达什么意思, 从标准库源代码is_nothrow_move_constructible<_Tp>::value 是判断_TP这个类型是否有不抛一场的移动构造方法, is_copy_constructible<_Tp>::value 并且拷贝构造方法,
源码看到这里大家心里就很清楚了, 到底咋回事!
相关文章:
c++ 移动构造方法为什么要加noexcept
背景: 最近看了候捷老师的c的教程, 他说移动构造方法要加noexcept, 在vector扩容的时候, 如果有移动构造方法没有加noexcept,是不会调用的. 个人感觉有些神奇, 这就去查下一探究竟. 过程: 测试代码如下: #include <iostream> #include <vector> struct A {A(){s…...
鸿鹄工程项目管理系统 Spring Cloud+Spring Boot+前后端分离构建工程项目管理系统
工程项目管理软件(工程项目管理系统)对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营,全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&am…...
手把手教你搭建园林园艺小程序商城
现如今,随着互联网的快速发展,小程序成为了企业和个人展示产品和服务的新方式。在园林园艺行业,构建一个园林园艺小程序能够更好地推广和销售自己的产品和服务。那么,如何构建一个园林园艺小程序呢?下面我们来详细介绍…...
Java Iterator(迭代器)
Java迭代器(Iterator)是 Java 集合框架中的一种机制,是一种用于遍历集合(如列表、集合和映射等)的接口。 它提供了一种统一的方式来访问集合中的元素,而不需要了解底层集合的具体实现细节。 Iterator 是 …...
Logstash同步MySQL数据到ElasticSearch
当MySQL数据到一定的数量级,而且索引不能实现时,查询就会变得非常缓慢,所以使用ElasticSearch来查询数据。本篇博客介绍使用Logstash同步MySQL数据到ElasticSearch,再进行查询。 测试环境 Windows系统MySQL 5.7Logstash 7.0.1El…...
【C++】运算符重载的示例实现和应用
C运算符重载的格式: operator 运算符 比如要重载 ! 运算符 : operator ! 下面是一个例子: class DemoText{DemoText(string str, int num){m_text str; m_number num;}string m_text;int m_number; }这里来定义两个对象:…...
Kubernetes禁止调度
在Kubernetes中,您可以通过几种方式来禁止某个Pod调度到节点上。以下是一些方法: Node Selector:您可以使用Node Selector来限制Pod只能调度到带有特定标签的节点上。如果您希望完全禁止Pod调度到某些节点上,可以确保这些节点不拥…...
CocosCreator3.8研究笔记(七)CocosCreator 节点和组件的介绍
相信很多新手朋友,肯定会问,CocosCreator 中什么是节点?什么是组件? 一、什么是组件(Component)? Cocos Creator 3.8 的工作流程是以组件式开发为核心,即以组合而非继承的方式进行游…...
Ceph入门到精通-C++入门知识点
C中的双冒号(::)是作用域分解运算符(scope resolution operator)。 它主要有以下两种用法: 用于区分同名的不同成员,例如在不同类中声明了同名的成员函数或成员变量,可以使用A::B的方式来特指A类的B成员。当全局变量…...
Ansible之playbook详解和应用实例
目录 一、playbook简介 1.什么是playbook 2.playbook组成 二、应用实例 1.使用playbook安装启用httpd服务 2.使用playbook安装启用nginx服务 三、ansible-playbook其他用法 1.检查yaml文件的语法是否正确 2.检查tasks任务 3.检查指定的主机 4.指定从某个task开始运行…...
经验萃取方法
【经验萃取】 经验萃取不是简单的总结提炼归纳! 经验萃取需经过还原、复盘分析、萃取重构 一.经验萃取前三个准备 1.定主题: 萃取主题选择(阐述原因、确定级别、差距/问题是源头)->多维评分:普遍性、重要性、迫切…...
手写apply方法
<script>/** 手写apply方法 * */Function.prototype.myApply function (context, args) {console.log(this, sss)//fnconst key Symbol()context[key] thiscontext[key](...args)delete context[key]return context[key]}const obj {name: zs,age: 18}function fn …...
Jenkins实现基础CD操作
操作截图 在Jenkins里面设置通过标签进行构建 在Jenkins中进入项目,配置以下 将execute shell换到invoke top-level maven targets之前 在gitlab中配置标签 代码迭代新的版本 项目代码迭代 修改docker-compose.yml 提交新版本的代码 在Jenkins中追加新…...
开源软件合集(Docker)
Docker安装 1.安装命令:curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun2.启动:systemctl start docker3.停止:systemctl stop docker4.重启:systemctl restart docker5.开机启动:systemctl enab…...
Ceph入门到精通-生产日志级别设置
Ceph 子系统及其日志记录级别的信息。 了解 Ceph 子系统及其日志记录级别 Ceph 由多个子系统组成: 每个子系统都有其日志记录级别: 默认情况下存储在 /var/log/ceph/ 目录中的输出日志(日志级别)存储在内存缓存中的日志&#…...
16-MyCat
一 Mycat概述 1 什么是Mycat 什么是Mycat Mycat是数据库中间件,所谓数据库中间件是连接Java应用程序和数据库中间的软件。 为什么要用Mycat 遇到问题: Java与数据库的紧耦合高访问量高并发对数据库的压力读写请求数据不一致 2 Mycat与其他中间件区别 目…...
RKNPU2通用API和零拷贝API
RKNPU2通用API 通用API接口按照异构编程规范,需要将数据拷贝到NPU运行时的内存空间。 通用API部署流程 初始化上下文,需要先创建上下文对象和读取模型文件 rknn_context ctx; model load_model(model_path, &model_len); ret rknn_init(&ctx…...
LeetCode 1123. 最深叶节点的最近公共祖先:DFS
【LetMeFly】1123.最深叶节点的最近公共祖先 力扣题目链接:https://leetcode.cn/problems/lowest-common-ancestor-of-deepest-leaves/ 给你一个有根节点 root 的二叉树,返回它 最深的叶节点的最近公共祖先 。 回想一下: 叶节点 是二叉树…...
多线程应用——线程池
线程池 文章目录 线程池1.什么是线程池2.为什么要用线程池3.怎么使用线程池4.工厂模式5.自己实现一个线程池6.创建系统自带的线程池6.1 拒绝策略6.2 线程池的工作流程 1.什么是线程池 字面意思,一次创建多个线程,放在一个池子(集合类),用的时…...
OPENCV+QT环境配置
【qtopencv开发入门:4步搞定opencv环境配置2】https://www.bilibili.com/video/BV1f34y1v7t8?vd_source0aeb782d0b9c2e6b0e0cdea3e2121eba 第一步: 安装QT Qt 5.15 第二步: 安装OPENCV VS2022 Opencv4.5.5 C 配置_愿飞翔的鱼儿的博客…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
