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 并且拷贝构造方法, 

源码看到这里大家心里就很清楚了, 到底咋回事!

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

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

相关文章

Lodash库

Lodash库 1.简介 Lodash 是一个 JavaScript 工具库&#xff0c;提供了许多实用的函数和方法&#xff0c;用于简化 JavaScript 编程的常见任务&#xff0c;例如数组操作、对象操作、函数处理、字符串处理、事件处理等。Lodash 提供了跨浏览器兼容性的工具函数&#xff0c;能够…

Unity中Shader的遮罩的实现

文章目录 前言一、遮罩效果的实现主要是使用对应的纹理实现的&#xff0c;在属性中暴露对应的遮罩纹理&#xff0c;对其进行采样后&#xff0c;最后相乘输出即可二、如果需要像和主要纹理一样流动&#xff0c;则需要使用和_Time篇一样的方法实现流动即可 前言 Unity中Shader的…

protues仿真时有时候串口虚拟中端不弹窗的问题

在使用proteus的时候&#xff0c;有时候你会发现点击调试开始运行后&#xff0c;串口虚拟终端没有自动弹窗的问题&#xff0c;其实照成这种现象的原因是你在使用的过程中移动了器件位置或者是对整个视窗使用鼠标滚动进行缩放了&#xff0c;如果要重新弹窗则需要进行以下操作: …

Echart笔记

Echart笔记 柱状图带背景色的柱状图将X与Y轴交换制作为进度条 柱状图 带背景色的柱状图 将X与Y轴交换制作为进度条 //将X与Y轴交换制作为进度条 option { xAxis: {type: value,min:0,max:100,show:false,//隐藏x轴},yAxis: {type: category,data:[进度条],show:false,//隐…

【阅读笔记】Graph of Thoughts: Solving Elaborate Problems with Large Language Models

Graph of Thoughts: Solving Elaborate Problems with Large Language Models Website & code: https://github.com/spcl/graph-of-thoughts 作者介绍了Graph of Thought (GoT)&#xff1a;一个具备提高LLM提示能力&#xff0c;超越了思维链或思维树 (ToT) 等范式提供的能…

大数据Flink(七十一):SQL的时间属性

文章目录 SQL的时间属性 一、Flink三种时间属性简介

MPI之通信模式(标准,缓存,同步,就绪)

MPI缓冲区 由MPI自行维护的一块内存区域&#xff0c;也可由用户(MPI_Bsend)自行维护&#xff1b;发送方 维护一块发送缓冲区&#xff1b; 接收方 维护一块接收缓冲区。 数据收发过程&#xff1a; 当发送端将数据拷贝到自身的数据缓冲区后(注意这里是拷贝&#xff0c;即数据到…

Unity中Shader的帧缓存区Clear(color+Z+stencil)

文章目录 前言一、什么是帧缓冲区二、片段运算三、随机扫描显示器&#xff08;可以按照自定义路径绘制帧&#xff09;四、光栅扫描显示器&#xff08;从左到右&#xff0c;从上到下&#xff0c;依次绘制&#xff09;五、缓冲的方式&#xff1a;单缓冲 和 双缓冲1、单缓冲2、双缓…

oled--SSD1315驱动

OLED 接口方式&#xff08;由硬件电路确定&#xff09;&#xff1a;6800、8080、spi、i2c. 常见的驱动芯片&#xff1a;ssd1306、ssd1315。 oled屏幕的发光原理不同于lcd&#xff0c;上电后无法直接显示&#xff0c;需要初始化后才能正常显示。 SSD1315手册资料 SSD1315是一款…

pom.xml配置文件失效,显示已忽略的pom.xml --- 解决方案

现象&#xff1a; 在 Maven 创建模块Moudle时,由于开始没有正确创建好&#xff0c;所以把它删掉了&#xff0c;然后接着又创建了与一个与之前被删除的Moudle同名的Moudle时&#xff0c;出现了 Ignore pom.xml&#xff0c;并且新创建的 Module 的 pom.xml配置文件失效&#xf…

OpenGL-入门-BMP像素图glReadPixels(1)实现读取屏幕中间的颜色和获取屏幕上鼠标点击位置的颜色

glReadPixels函数用于从帧缓冲区中读取像素数据。它可以用来获取屏幕上特定位置的像素颜色值或者获取一块区域内的像素数据。下面是该函数的基本语法&#xff1a; void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *da…

质谱技术对蛋白质进行鉴定

参考B站教学视频: 质谱如何鉴定蛋白质_哔哩哔哩_bilibili 针对该视频&#xff0c;别人的 笔记 质谱是一台体重秤&#xff0c;称的不是人&#xff0c;而是分子、原子的体重 不同分子有不同分子量是质谱仪工作的底层逻辑 图片来自&#xff1a;【蛋白组】蛋白质组定量技术的原理和…

C++的单例模式

忘记之前有没有写过单例模式了。 再记录一下&#xff1a; 我使用的代码&#xff1a; #ifndef SINGLETON_MACRO_HPP #define SINGLETON_MACRO_HPP#define SINGLETON_DECL(class_name) \ public: \static class_name& instance() { \static class_name s_instance; \return …

【UE 材质】实现方形渐变、中心渐变材质

步骤 一、实现方形渐变 1. 新建一个材质&#xff0c;材质域选择“后期处理” 2. 通过“Mask”节点单独获取R、G通道&#xff0c;可以看到R通道是从左到右0~1之间的变化&#xff0c;对应U平铺 可以看到G通道是从上到下0~1之间的变化&#xff0c;对应V平铺 3. 完善如下节点 二、…

vim 使用/进行关键字搜索,跳转到下一个搜索结果的快捷键

在命令行中使用 / 或 ? 向下或向上搜索指定的字符&#xff0c;字符可手动输入 或 粘贴指定寄存器里的字符输入。通过 n 或 N 继续原方向或反向跳转到下一个匹配到的位置。 此外还可以同时进行多个匹配(关键字)的搜索&#xff0c;不同匹配项之间使用\|隔开即可。只要找到满足这…

里氏替换原则在继承关系中子类对父类方法的重写(覆盖)或重载时应遵循的规则

什么是里氏替换原则&#xff1a;只要父类能出现的地方子类就可以出现&#xff0c;而且 替换为子类也不会产生任何错误或异常&#xff0c;使用者可能根本就不需要知道是父类还是子类。但是&#xff0c;反过来就不行了&#xff0c;有子类出现的地方&#xff0c;父类未必就能适应。…

非关系型数据库Redis的安装

一、关系型数据库与非关系型数据库的区别&#xff1a;---------面试高频率问题 1、首先了解一下 什么是关系型数据库&#xff1f; 关系型数据库最典型的数据结构是表&#xff0c;由二维表及其之间的联系所组成的一个数据组织。 优点&#xff1a; 易于维护&#xff1a;都是使用…

2023年高教社杯数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 最短时…

数据分析作业2

中国在 2020 年开展第七次全国人口普查&#xff0c;截止 2021 年 5 月 11 日普查结果公布&#xff0c;全国人口共1411778724人。单从数据表格看相关数据不够直观&#xff0c;需要进行数据可视化展示&#xff0c;方便查看数据结果。 任务一&#xff1a;链接 MySQL 数据库&#x…

【C++设计模式】依赖倒转原则

2023年8月30日&#xff0c;周三上午 目录 概述含义举个简单的例子传统做法使用依赖倒转原则代码说明再举一个具体的例子以生活为例 概述 依赖倒转原则(Dependency Inversion Principle,DIP)是面向对象设计中的一个基本原则。 含义 高层模块不应该依赖低层模块,两者都应该依…