转移和完美转发

news2024/12/18 10:52:22

转移和完美转发

文章目录

  • 转移和完美转发
    • std::move
      • 作用
      • 使用方法
    • std::forward
      • 作用
      • 使用方法

std::move

std::move 是 C++11 引入的一个标准库函数,主要用于将一个对象标记为“可移动”的状态,从而允许资源的转移而不是复制。

作用

  • 资源转移:当一个对象被 std::move 标记后,编译器会尝试使用移动语义(move semantics)来转移资源,而不是进行深拷贝。
  • 性能优化:对于大型对象或包含动态分配资源的对象,使用 std::move 可以显著提高性能,因为移动操作通常比复制操作更高效。
  • 使用std::move方法可以将左值转换为右值。使用这个函数并不能移动任何东西,而是和移动构造函数一样都具有移动语义,将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存拷贝。

使用方法

  1. 基本语法:
T&& rvalue = std::move(lvalue);

其中 lvalue 是一个左值(具有持久生命周期的对象),std::move 将其转换为右值引用(rvalue reference)。

  1. 使用方法:
class Test
{
publicTest(){}
    ......
}
int main()
{
    Test t;
    Test && v1 = t;          // error
    Test && v2 = move(t);    // ok
    return 0;
}

1. 在第10行中,使用左值初始化右值引用,因此语法是错误的
2. 在第11行中,使用move()函数将左值转换为了右值,这样就可以初始化右值引用了。

假设一个临时容器很大,并且需要将这个容器赋值给另一个容器,就可以执行如下操作:

list<string> ls;
ls.push_back("hello");
ls.push_back("world");
......
list<string> ls1 = ls;        // 需要拷贝, 效率低
list<string> ls2 = move(ls);

如果不使用std::move,拷贝的代价很大,性能较低。使用move几乎没有任何代价,只是转换了资源的所有权。如果一个对象内部有较大的堆内存或者动态数组时,使用move()就可以非常方便的进行数据所有权的转移。另外,我们也可以给类编写相应的移动构造函数(T::T(T&& another))和和具有移动语义的赋值函数(T&& T::operator=(T&& rhs)),在构造对象和赋值的时候尽可能的进行资源的重复利用,因为它们都是接收一个右值引用参数。

std::forward

std::forward 是 C++11 引入的一个标准库函数,主要用于完美转发(perfect forwarding)。完美转发是指在模板函数中,将参数原封不动地传递给另一个函数,保留其左值或右值属性。

作用

  • 保留参数类型:确保参数在传递过程中保持其原始的左值或右值特性。(T:参数)
    • 当T为左值引用类型时,t将被转换为T类型的左值
    • 当T不是左值引用类型时,t将被转换为T类型的右值
  • 灵活性:使得模板函数能够处理不同类型的参数,而不会丢失其原始属性。

使用方法

  1. 基本语法:
T&& forward(remove_reference_t<T>& t) noexcept;
T&& forward(remove_reference_t<T>&& t) noexcept;

其中 T 是模板参数类型。

  1. 示例: 假设有一个模板函数 foo,它接受一个通用参数并将其传递给另一个函数 bar
template <typename T>
void foo(T&& param) {
    bar(std::forward<T>(param));
}

void bar(int& x) {
    std::cout << "Lvalue reference" << std::endl;
}

void bar(int&& x) {
    std::cout << "Rvalue reference" << std::endl;
}

int main() {
    int x = 42;
    foo(x);        // 输出: Lvalue reference
    foo(42);       // 输出: Rvalue reference
    return 0;
}
  1. 下面通过一个例子演示一下关于forward的使用:
#include <iostream>
using namespace std;

template<typename T>
void printValue(T& t)
{
    cout << "l-value: " << t << endl;
}

template<typename T>
void printValue(T&& t)
{
    cout << "r-value: " << t << endl;
}

template<typename T>
void testForward(T && v)
{
    printValue(v);
    printValue(move(v));
    printValue(forward<T>(v));
    cout << endl;
}

int main()
{
    testForward(520);
    int num = 1314;
    testForward(num);
    testForward(forward<int>(num));
    testForward(forward<int&>(num));
    testForward(forward<int&&>(num));

    return 0;
}
===== 输出:
l-value: 520
r-value: 520
r-value: 520

l-value: 1314
r-value: 1314
l-value: 1314

l-value: 1314
r-value: 1314
r-value: 1314

l-value: 1314
r-value: 1314
l-value: 1314

l-value: 1314
r-value: 1314
r-value: 1314
1. testForward(520);函数的形参为未定引用类型T&&,实参为右值,初始化后被推导为一个右值引用
    1. printValue(v);已命名的右值v,编译器会视为左值处理,实参为左值
    2. printValue(move(v));已命名的右值编译器会视为左值处理,通过move又将其转换为右值,实参为右值
    3. printValue(forward<T>(v));forward的模板参数为右值引用,最终得到一个右值,实参为 右值
2. testForward(num);函数的形参为未定引用类型T&&,实参为左值,初始化后被推导为一个左值引用  
    1. printValue(v);实参为左值
    2. printValue(move(v));通过move将左值转换为右值,实参为右值
    3. printValue(forward<T>(v));forward的模板参数为左值引用,最终得到一个左值引用,实参为左值
3. testForward(forward<int>(num));forward的模板类型为int,最终会得到一个右值,函数的形参为未定引用类型T&&被右值初始化后得到一个右值引用类型
    1. printValue(v);已命名的右值v,编译器会视为左值处理,实参为左值
    2. printValue(move(v));已命名的右值编译器会视为左值处理,通过move又将其转换为右值,实参为右值
    3. printValue(forward<T>(v));forward的模板参数为右值引用,最终得到一个右值,实参为右值
4. testForward(forward<int&>(num));forward的模板类型为int&,最终会得到一个左值,函数的形参为未定引用类型T&&被左值初始化后得到一个左值引用类型
    1. printValue(v);实参为左值
    2. printValue(move(v));通过move将左值转换为右值,实参为右值
    3. printValue(forward<T>(v));forward的模板参数为左值引用,最终得到一个左值,实参为左值
5. testForward(forward<int&&>(num));forward的模板类型为int&&,最终会得到一个右值,函数的形参为未定引用类型T&&被右值初始化后得到一个右值引用类型
    1. printValue(v);已命名的右值v,编译器会视为左值处理,实参为左值
    2. printValue(move(v));已命名的右值编译器会视为左值处理,通过move又将其转换为右值,实参为右值
    3. printValue(forward<T>(v));forward的模板参数为右值引用,最终得到一个右值,实参为右值

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

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

相关文章

一、LRU缓存

LRU缓存 1.LRU缓存介绍2.LRU缓存实现3.LRU缓存总结3.1 LRU 缓存的应用3.2 LRU 缓存的优缺点 1.LRU缓存介绍 LRU是Least Recently Used 的缩写&#xff0c;意为“最近最少使用”。它是一种常见的缓存淘汰策略&#xff0c;用于在缓存容量有限时&#xff0c;决定哪些数据需要被删…

【大前端vue:组件】鼠标上移 出现动画

【大前端vue&#xff1a;组件】鼠标上移 出现动画 <template><div class"view-introduction-culture"><div class"culture-wrapper"><h2 class"culture-title">鼠标上移&#xff1a;展示动画 显示出来</h2><di…

解决电脑网速慢问题:硬件检查与软件设置指南

电脑网速慢是许多用户在使用过程中常见的问题&#xff0c;它不仅会降低工作效率&#xff0c;还可能影响娱乐体验。导致电脑网速慢的原因多种多样&#xff0c;包括硬件问题、软件设置和网络环境等。本文将从不同角度分析这些原因&#xff0c;并提供提高电脑网速的方法。 一、检查…

快速本地化部署 OnlyOffice服务 ( Linux+Docker)

文章目录 一、OnlyOffice介绍&#x1f4d6;二、集成OnlyOffice&#x1f9e9;2.1 环境准备&#x1f5a5;️2.2 搜索镜像2.3 拉取镜像2.4 查看镜像2.5 创建容器2.6 进入容器配置2.7 重启服务2.8 添加字体字号2.9 测试OnlyOffice服务 三、在线预览office文档四、Cpolar内网穿透 一…

Leecode刷题C++之形成目标字符串需要的最少字符串数①

执行结果:通过 执行用时和内存消耗如下&#xff1a; 代码如下&#xff1a; class Solution { public:int minValidStrings(vector<string>& words, string target) {auto prefix_function [](const string& word, const string& target) -> vector<…

51c嵌入式~合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/11529950 一、不同的电平信号的MCU怎么通信 “电平转换”电路 ​ 先说一说这个电路的用途&#xff1a;当两个MCU在不同的工作电压下工作&#xff08;如MCU1 工作电压5V&#xff1b;MCU2 工作电压3.3V&#xff09;&#xf…

2024年第十五届蓝桥杯青少组C++国赛—割点

割点 题目描述 一张棋盘由n行 m 列的网格矩阵组成&#xff0c;每个网格中最多放一颗棋子。当前棋盘上已有若干棋子。所有水平方向或竖直方向上相邻的棋子属于同一连通块。 现给定棋盘上所有棋子的位置&#xff0c;如果要使棋盘上出现两个及以上的棋子连通块&#xff0c;请问…

Java线程池解读

Java 线程池是一个提供多线程管理和调度的工具&#xff0c;通常用来处理多个并发任务。线程池能够帮助有效管理线程的创建、调度、执行和销毁&#xff0c;避免频繁的线程创建和销毁&#xff0c;提高系统性能。 前言 Java 线程池是面试中的常客&#xff0c;面试官经常会问线程…

如何为IntelliJ IDEA配置JVM参数

在使用IntelliJ IDEA进行Java开发时&#xff0c;合理配置JVM参数对于优化项目性能和资源管理至关重要。IntelliJ IDEA提供了两种方便的方式来设置JVM参数&#xff0c;以确保你的应用程序能够在最佳状态下运行。本文将详细介绍这两种方法&#xff1a;通过工具栏编辑配置和通过服…

【CC2530开发基础篇】继电器模块使用

一、前言 1.1 开发背景 本实验通过使用CC2530单片机控制继电器的吸合与断开&#xff0c;深入了解单片机GPIO的配置与应用。继电器作为一种常见的电气控制元件&#xff0c;广泛用于自动化系统中&#xff0c;用于控制大功率负载的开关操作。在本实验中&#xff0c;将通过GPIO口…

通过解调使用正则化相位跟踪技术进行相位解包裹

1. 绪论 光学计量学通常使用光学干涉仪来测量各种物理量。1,2 根据应用的不同&#xff0c;可以使用多种类型的干涉仪&#xff0c;但它们的共同目标是产生一个由被测物理量调制的条纹图案。使用这种光束编码程序可以检测到的物理量范围非常广&#xff1a;深度测量、应变分析、温…

数字图像处理技术期末复习

1. 已知图像的分辨率和深度&#xff0c;怎么求图像的存储空间&#xff08;位&#xff0c;字节&#xff0c;KB&#xff09;&#xff1f; 题目&#xff1a; 已知图像的分辨率和深度&#xff0c;怎么求图像的存储空间&#xff08;位&#xff0c;字节&#xff0c;KB&#xff09;&a…

Elasticsearch 架构及 Lucene 索引结构原理入门

文章目录 Elasticsearch 整体架构Lucene 索引结构Lucene 倒排索引核心原理倒排索引倒排表&#xff08;Posting List&#xff09; Elasticsearch 整体架构 一个 ES Index 在集群模式下&#xff0c;有多个Node&#xff08;节点&#xff09;组成&#xff0c;每个节点就是ES的 inst…

人脸检测的若干思考!!!

1.目前主要有人脸检测方法分类&#xff1f; 主要包含两类&#xff1a;传统人脸检测算法和基于深度学习的人脸检测算法。 传统人脸检测算法主要可以分为4类&#xff1a; 基于知识、模型、特征和外观的人脸检测方法&#xff1b; 基于深度学习的方法&#xff1a;基于级联CNN的人脸…

突破时间与空间限制的富媒体百宝箱——智能工具箱:让云上内容生产更easy

“这是你的同款日常吗&#xff1f;老是在赶deadline&#xff0c;苦练PS还未出师&#xff0c;premiere、达芬奇真的好难&#xff0c;学python脑容量确实不够~打工人太难了~~” 来试试智能工具箱吧&#xff01;即来即用&#xff0c;一键实现办公自由。图片工具、视频工具、音频工…

el-table打印PDF预览,表头错位的解决方案

文章目录 背景与需求需求分析解决方案方案一&#xff1a;vue-print-nb插件安装引入使用 方案二安装使用 方案三 总结 背景与需求 本例以vue2项目为例&#xff0c;vue3与react等同理。 有个项目需要打印的功能&#xff0c;网页使用vue2写的&#xff0c;主体内容为表格el-table&a…

【算法day16】二叉树:搜索二叉树的修剪与构建

题目引用 修剪二叉搜索树将有序数组转换为二叉搜索树把二叉搜索树转换为累加树 1. 修剪二叉搜索树 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在…

java中File类

1、介绍 File类定义了一些与平台无关的方法来操作文件&#xff0c;可以通过调用File类中的方法&#xff0c;实现创建、删除、重命名文件等操作。File类的对象主要用来获取文件本身的一些信息&#xff0c;如文件所在的目录、文件长度、文件读写权限等。数据流可以将数据写入到文…

金碟中间件-AAS-V10.0安装

金蝶中间件AAS-V10.0 AAS-V10.0安装 1.解压AAS-v10.0安装包 unzip AAS-V10.zip2.更新license.xml cd /root/ApusicAS/aas# 这里要将license复制到该路径 [rootvdb1 aas]# ls bin docs jmods lib modules templates config domains …

易语言OCR证件照文字识别

一.引言 文字识别&#xff0c;也称为光学字符识别&#xff08;Optical Character Recognition, OCR&#xff09;&#xff0c;是一种将不同形式的文档&#xff08;如扫描的纸质文档、PDF文件或数字相机拍摄的图片&#xff09;中的文字转换成可编辑和可搜索的数据的技术。随着技…