C++14之std::exchange的使用和原理分析

目录

1.概述

2.使用

2.1.交换操作

2.2.移动语义

3.原理

4.综合示例

5.总结


1.概述

   std::exchange 是 C++ 标准库中的一个实用函数,它的主要作用是替换一个对象的值,并返回该对象的旧值。这个函数在 C++14 中引入,主要用于简化和优化代码。

        它的原型定义如下:

        这个函数接受两个参数:一个是要替换值的对象 obj,另一个是新的值 new_value。函数将 obj 的值替换为 new_value,并返回 obj 的旧值。

注意: T 必须满足可移动构造 (MoveConstructible) 。而且必须能移动赋值 U 类型对象给 T 类型对象

2.使用

2.1.交换操作

        在std::exchage未出现之前, 我们交换两个变量的值,需要先定义一个临时的中间变量,但是使用std::exchange,你可以更简洁地完成这个操作;这对于实现一些特定的算法,尤其是需要保持变量旧值的算法时非常有用。示例如下:

#include <iostream>
#include <utility>
#include <string>int main() {std::string name = "Alice";std::string new_name = "Bob";std::string old_name = std::exchange(name, new_name);std::cout << "Old name: " << old_name << std::endl;std::cout << "New name: " << name << std::endl;return 0;
}

2.2.移动语义

std::exchange在处理移动语义时非常有用。例如,你可能想要在类的移动构造函数或移动赋值运算符中使用它:

class MyClass {
public:// Move constructorMyClass(MyClass&& other) noexcept : ptr_(std::exchange(other.ptr_, nullptr)) {}// Move assignment operatorMyClass& operator=(MyClass&& other) noexcept {if (this != &other) {delete ptr_;  // delete resourceptr_ = std::exchange(other.ptr_, nullptr);  // acquire new resource}return *this;}private:int* ptr_;
};

在这个例子中,std::exchange用于获取other的资源(ptr_),并将other.ptr_设置为nullptr,以防止other在析构时删除资源。

3.原理

std::exchange 的实现原理相对简单。它首先返回 obj 的当前值(即“旧值”),然后将 obj 的值设置为 new_val。这通常是通过原子操作完成的,以确保在多线程环境中操作的原子性。然而,具体的实现可能因编译器和平台而异。

在 vs2019 中,std::exchange 的实现如下:

// FUNCTION TEMPLATE exchange
template <class _Ty, class _Other = _Ty>
_CONSTEXPR20 _Ty exchange(_Ty& _Val, _Other&& _New_val) noexcept(conjunction_v<is_nothrow_move_constructible<_Ty>, is_nothrow_assignable<_Ty&, _Other>>) /* strengthened */ {// assign _New_val to _Val, return previous _Val_Ty _Old_val = static_cast<_Ty&&>(_Val);_Val         = static_cast<_Other&&>(_New_val);return _Old_val;
}

这里有几个关键点:

1) noexcept(conjunction_v<is_nothrow_move_constructible<_Ty>, is_nothrow_assignable<_Ty&, _Other>>)确保如果赋值操作是 noexcept 的,那么整个 std::exchange 函数也是 noexcept 的。这有助于优化和异常安全性。conjunction_v是逻辑与。

2) static_cast<_Ty&&>(_Val)相当于std::move(_Val) 用于获取 _Val 的旧值,并将其转换为右值引用,以便在可能的情况下利用移动语义。

3)_Val = static_cast<_Other&&>(_New_val); 将新值赋给 _Val 相当于_Val  = std::forward<_Other>(_New_val)。std::forward 用于保持 _New_val的值类别(左值或右值)。

4.综合示例

#include <iostream>
#include <iterator>
#include <utility>
#include <vector>class stream
{
public:using flags_type = int;public:flags_type flags() const { return flags_; }/// 以 newf 替换 flags_ 并返回旧值。flags_type flags(flags_type newf) { return std::exchange(flags_, newf); }private:flags_type flags_ = 0;
};void f() { std::cout << "f()"; }int main()
{stream s;std::cout << s.flags() << '\n';std::cout << s.flags(12) << '\n';std::cout << s.flags() << "\n\n";std::vector<int> v;// 因为第二模板形参有默认值,故能以花括号初始化式列表为第二实参。// 下方表达式等价于 std::exchange(v, std::vector<int>{1, 2, 3, 4});std::exchange(v, {1, 2, 3, 4});std::copy(begin(v), end(v), std::ostream_iterator<int>(std::cout, ", "));std::cout << "\n\n";void (*fun)();// 模板形参的默认值亦使得能以通常函数为第二实参。// 下方表达式等价于 std::exchange(fun, static_cast<void(*)()>(f))std::exchange(fun, f);fun();std::cout << "\n\n斐波那契数列: ";for (int a{0}, b{1}; a < 100; a = std::exchange(b, a + b))std::cout << a << ", ";std::cout << "...\n";
}

输出:

0
0
121, 2, 3, 4,f()斐波那契数列: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

5.总结

1) 原子性:std::exchange 提供了原子操作,这意味着在交换过程中,其他线程不会看到中间状态。这对于需要精确控制数据状态的多线程应用程序非常有用。
2) 返回值:std::exchange 返回交换前的值。这允许你在一个操作中同时获取和设置值。
3) 用途广泛:除了直接的数据交换,std::exchange 还可以与算法和其他模板一起使用,以提供更复杂的操作。
4) 与 std::swap 的区别:std::swap 也是用于交换两个值的函数,但它不保证原子性。在单线程环境中,它们可以互换使用,但在多线程环境中,std::exchange 更为安全。
5) 性能考虑:由于 std::exchange 提供了原子性保证,它可能比非原子操作(如简单的赋值)更慢。因此,在不需要原子性的情况下,使用简单的赋值或其他非原子操作可能更为高效。
6) 与 C++ 标准库的其他部分集成:std::exchange 可以与 C++ 的其他部分(如算法、容器和迭代器)无缝集成,提供灵活的编程选项。
        总之,std::exchange 是一个强大且灵活的工具,可以用于在 C++ 中进行原子值交换。然而,在使用它时,需要权衡其提供的原子性与可能的性能影响。

参考

std::exchange - cppreference.com

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

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

相关文章

OpenCV如何在图像中寻找轮廓

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV如何模板匹配 下一篇 :OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数 cv::findContour…

55.基于SpringBoot + Vue实现的前后端分离-旅游管理系统(项目 + 论文)

项目介绍 本站是一个B/S模式系统&#xff0c;采用SpringBoot Vue框架&#xff0c;MYSQL数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得基于SpringBoot Vue技术的旅游管理系统设计与实现管理工作系统…

使用STM32CubeMX对STM32F4进行串口配置

目录 1. 配置1.1 Pin脚1.2 RCC开启外部晶振1.3 时钟1.4 串口配置 2. 代码2.1 默认生成代码2.1 开启串口中断函数2.3 接收中断2.4 接收回调函数2.5 增加Printf 的使用 1. 配置 1.1 Pin脚 1.2 RCC开启外部晶振 1.3 时钟 外部使用8MHz晶振 开启内部16MHz晶振 使用锁相环 开启最高…

ROS2 学习笔记(二)常用小工具

1. rqt_console #启动 ros2 run rqt_console rqt_console日志级别&#xff1a;Fatal --> Error --> Warn --> Info --> Debug #修改允许发布的日志级别 ros2 run <package_name> <executable_name> --ros-args --log-level WARN2. launch文件 ROS2中…

Outlook大附件插件 有效解决附件大小限制问题

很多企业都是使用Outlook来进行邮件的收发&#xff0c;可是由于附件大小有限&#xff0c;导致很多大文件发不出去&#xff0c;就会产生Outlook大附件插件这种业务需求。 邮件系统在发送大文件时面临的限制问题主要如下&#xff1a; 1、附件大小限制&#xff1a;大多数邮件服务…

深度学习基础之《TensorFlow框架(16)—神经网络案例》

一、mnist手写数字识别 1、数据集介绍 mnist数据集是一个经典的数据集&#xff0c;其中包括70000个样本&#xff0c;包括60000个训练样本和10000个测试样本 2、下载地址&#xff1a;http://yann.lecun.com/exdb/mnist/ 3、文件说明 train-images-idx3-ubyte.gz: training s…

Hbase学习笔记

Hbase是什么 HBase是一个高可靠、高性能、面向列、可伸缩的分布式存储系统。它利用Hadoop HDFS作为其文件存储系统,并提供实时的读写的数据库系统。HBase的设计思想来源于Google的BigTable论文,是Apache的Hadoop项目的子项目。它适合于存储大表数据,并可以达到实时级别。HB…

【Redis 开发】Lua语言

Lua Lua语法 Lua语法 Lua是一种小巧的脚本语言&#xff0c;底层用C语言实现&#xff0c;为了嵌入式应用程序中 官网&#xff1a;https://www.lua.org/ 创建lua文件 touch hello.lua 运行lua文件 lua hello.lua 输出语句 print("Hello World!")数据类型 可以通过t…

一篇易懂的SPI通讯指南

SPI概念 SPI&#xff08;Serial Peripheral interface, 串行外设接口&#xff09;是微处理控制单元(MCU)和外围IC&#xff08;如传感器、ADC、DAC、驱动芯片和外部存储设备等&#xff09;之间进行通信的同步串行端口&#xff0c;其通信速率一般可以从几千bps到几百Mbps甚至更高…

QT httpServer多线程后台服务器的例子实现

1.需求 1.1 用户需要其他平台&#xff08;web端&#xff09;调用Qt平台的接口&#xff0c;获取想要的数据并实时显示在网页里&#xff0c;比如实时的温湿度&#xff0c;用户数据等 1.2 用户需要在其他平台&#xff08;web端&#xff09;调用Qt平台的接口&#xff0c;下发数据…

kettle从入门到精通 第五十五课 ETL之kettle Excel输入

想真正学习或者提升自己的ETL领域知识的朋友欢迎进群&#xff0c;一起学习&#xff0c;共同进步。 1、 Excel输入&#xff0c;Microsoft Excel输入步骤的作用是从Microsoft Excel中读取数据&#xff0c;如下图所示&#xff1a; 1&#xff09;Excel输入步骤从文件D:\data\测试数…

Linux实现简单进度条(附原理解释和动图效果)

1&#xff0c;行缓冲区 先看下面的代码和运行结果&#xff0c; #include<stdio.h> #include<unistd.h> int main() {printf("你好\n");sleep(3);return 0; }只是一个简单的打印“你好”然后休眠三秒&#xff0c;最后程序结束 再看下面的代码和运行结果…

Docker-Compose单机多容器应用编排与管理

前言 Docker Compose 作为 Docker 生态系统中的一个重要组件&#xff0c;为开发人员提供了一种简单而强大的方式来定义和运行多个容器化应用。本文将介绍 Docker Compose 的使用背景、优劣势以及利用 Docker Compose 简化应用程序的部署和管理。 目录 一、Docker Compose 简…

CUDA架构介绍与设计模式解析

文章目录 **CUDA**架构介绍与设计模式解析**1** CUDA 介绍CUDA发展历程CUDA主要特性CUDA系统架构CUDA应用场景编程语言支持CUDA计算过程线程层次存储层次 **2** CUDA 系统架构分层架构并行计算模式生产-消费者模式工作池模式异步编程模式 **3** CUDA 中的设计模式工厂模式策略模…

闲话 Asp.Net Core 数据校验(三)EF Core 集成 FluentValidation 校验数据例子

前言 一个在实际应用中 EF Core 集成 FluentValidation 进行数据校验的例子。 Step By Step 步骤 创建一个 Asp.Net Core WebApi 项目 引用以下 Nuget 包 FluentValidation.AspNetCore Microsoft.AspNetCore.Identity.EntityFrameworkCore Microsoft.EntityFrameworkCore.Re…

SQL事前巡检插件

背景: 事故频发 •在工作过程中每年都会看到SQL问题引发的线上问题&#xff0c;一条有问题的SQL足以拖垮整个数据库 不易发觉 •对于SQL性能问题测试在预发环境不易发现&#xff08;数据量小&#xff09; •SAAS系统隔离字段在SQL条件中遗漏&#xff0c;造成越权风险 •业…

【算法刷题 | 贪心算法08】4.29(划分字母区间、合并区间)

文章目录 14.划分字母区间14.1题目14.2解法&#xff1a;贪心14.2.1贪心思路14.2.2代码实现 15.合并区间15.1题目15.2解法&#xff1a;贪心15.2.1贪心思路15.2.2代码实现 14.划分字母区间 14.1题目 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段&#xff0c;同一…

WebSocket 全面解析

&#x1f31f; 引言 WebSocket&#xff0c;一个让实时通信变得轻而易举的神器&#xff0c;它打破了传统HTTP协议的限制&#xff0c;实现了浏览器与服务器间的全双工通信。想象一下&#xff0c;即时消息、在线游戏、实时股票报价…这一切都离不开WebSocket的魔力&#x1f4ab;。…

LT6911UXE HDMI 2.0 至双端口 MIPI DSI/CSI,带音频 龙迅方案

1. 描述LT6911UXE 是一款高性能 HDMI2.0 至 MIPI DSI/CSI 转换器&#xff0c;适用于 VR、智能手机和显示应用。HDMI2.0 输入支持高达 6Gbps 的数据速率&#xff0c;可为4k60Hz视频提供足够的带宽。此外&#xff0c;数据解密还支持 HDCP2.3。对于 MIPI DSI / CSI 输出&#xff0…

零基础HTML教程(30)--迈入HTML5新时代

文章目录 1. 从H4时代到H5时代2. 属性值可以不用引号3. 标签使用大小写均可4. 部分属性值可以省略5. 浏览器支持情况6. 小结 1. 从H4时代到H5时代 之前讲的29篇HTML教程&#xff0c;内容基本都是H4时代就有的。 随着时代的发展&#xff0c;H4多少有点不够用&#xff0c;所以H…