【C++】std::move与std::forward函数的区别

想了解它们之间的区别,得先了解一些概念,如下:
一. 前言:左值、右值、将亡值

左值:有一个明确的地址,可以被取地址的值,可以被赋值的变量(如变量、数组元素、解引用指针)。

cpp 复制代码
int a = 2;
int b = a;

//如上: b和a都是左值,2是右值

右值:无明确地址,无法被取地址的值(如数字,临时对象)。

cpp 复制代码
int x = 5; // 5是右值,x是左值

抽象概念: 可以放在=号左边的都是左值;放在=号右边的可能是左值也可能是右值;右值一定不能放在=号左边

将亡值:通常在完成移动资源的初始化或赋值后会被销毁的值;返回右值引用的函数调用表达式和转换为右值引用的表达式。
将亡值是显式标记为"可以移动"的对象,通常是 生命周期即将结束但还未销毁的对象。
它是右值的一个子集(rvalue = xvalue + prvalue,即 将亡值 + 纯右值)。

cpp 复制代码
 std::string s1 = "Hello";
 std::string&& s2 = std::move(s1);  // s1 现在是一个 xvalue(可以移动)

二. std::move()函数与std::forward()

简介1. std::move(): C++11 引入的一个函数,用于将一个左值转换为右值引用,从而触发对象的移动语义。它的核心作用是通过高效地转移资源,避免不必要的深拷贝操作(比如使用对象作为参数进行对象初始化时),从而提升程序性能。主要功能是将一个左值强制转换为右值引用,从而触发移动语义。它并不会真正移动对象,而是通过类型转换使资源的所有权可以被转移。无论传染参数是左/右值得到的结果都是右值

cpp 复制代码
#include <iostream>
#include <string>

std::string&& tempString()
{
	std::string a = "hello work.";
	return std::move(a);
}

int main() 
{
	std::string s = "Hello";
	std::string&& str1 = std::move(s);
	std::cout << "str1:" << str1 << "" << std::endl;
	std::cout << "s:" << s << "" << std::endl;
	s = "123";
	std::cout << "str1:" << str1 << "" << std::endl;
	std::cout << "s:" << s << "" << std::endl;
	str1 = "qwe";
	std::cout << "str1:" << str1 << "" << std::endl;
	std::cout << "s:" << s << "" << std::endl;
	//std::string&& str2 = tempString();
	//std::cout << "str2:" << str2 << "" << std::endl;
	return 0;
}

std::move(s) 只是将 s 强制转换为右值引用 (std::string&&),表示"s 可以被移动"。

它本身不会调用任何移动构造函数或析构函数,仅仅是类型转换。
为什么 s 的值仍然可访问?

在你当前的代码中,str1 只是 s 的右值引用别名 (这个是和引用一样的,都是共用一个的值,上面的打印就可以知道。),没有实际触发移动语义。

移动操作的发生条件:只有当右值引用被用于构造/赋值时(例如传递给移动构造函数或 std::string 的移动赋值运算符),才会真正"移动"资源。

如下是触发移动构造函数的例子:

cpp 复制代码
std::string s = "Hello";   
std::string&& str1 = std::move(s);  // 无实际移动,s 仍然有效
   
std::string s2 = std::move(s);      // 触发移动构造函数,s 的内容被"掏空"   
std::cout << s;                     // 此时 s 可能为空或状态无效!

关键点总结
std::move 只是标记 :它告诉编译器"这个对象可以被移动",但实际是否移动取决于后续操作。

右值引用绑定不触发移动:仅仅绑定引用 (如 std::string&&)不会改变原对象。
移动的"破坏性"需要显式触发 :必须通过移动构造/赋值等操作才会转移资源。

如下:

std::move(s);只是标记这个对象可以被移动,

std::string&& str1 = std::move(s); // 无实际移动,s 仍然有效,这个是不会触发构造函数,资源还在的。

std::string s2 = std::move(s); // 触发移动构造函数,s 的内容被"掏空" 。这个触发了std::string的移动构造函数。

std::forward():

用于实现完美转发,即在模板函数中保持参数的左值或右值属性不变。它可以根据传入参数的类型,决定是转发为左值还是右值。

cpp 复制代码
#include <iostream>   
#include <utility> // for std::forward
   
// 目标函数   
void log(const std::string& lval) {
    std::cout << "左值: " << lval << std::endl;   
}
   
void log(std::string&& rval) {
    std::cout << "右值: " << rval << std::endl;   
}
   
// 转发函数   
template <typename T>   
void relay(T&& arg) {
    log(std::forward<T>(arg)); // 完美转发   
}
   
int main() {
    std::string s = "Hello";
    relay(s);             // 转发左值
    relay(std::move(s));  // 转发右值(将亡值)
    relay("World");       // 转发右值(临时对象)
    return 0;
}


std::forward 的作用:

当模板参数 T 是左值引用(如 T = int&)时,std::forward 返回左值引用。

当 T 是普通类型(如 T = int)时,std::forward 返回右值引用。

保持参数的原始值类别(左值/右值)。

与 std::move 的区别:

std::move 无条件 转换为右值引用。

std::forward 有条件 转换,保留值类别。

使用场景:

模板函数中转发参数(如工厂模式、构造函数代理)。

需要同时处理左值和右值的泛型代码。

相关推荐
TDengine (老段)1 小时前
TDengine C/C++ 连接器进阶指南
大数据·c语言·c++·人工智能·物联网·时序数据库·tdengine
a3535413821 小时前
设计模式-代理模式
c++·设计模式·代理模式
TTGGGFF2 小时前
Supertonic 部署与使用全流程保姆级指南(附已部署镜像)
开发语言·python
木木木一2 小时前
Rust学习记录--C7 Package, Crate, Module
开发语言·学习·rust
love530love2 小时前
升级到 ComfyUI Desktop v0.7.0 版本后启动日志报 KeyError: ‘tensorrt‘ 错误解决方案
开发语言·windows·python·pycharm·virtualenv·comfyui·comfyui desktop
Evand J3 小时前
【MATLAB例程】【空地协同】UAV辅助的UGV协同定位,无人机辅助地面无人车定位,带滤波,附MATLAB代码下载链接
开发语言·matlab·无人机·无人车·uav·协同定位·ugv
chao1898443 小时前
基于MATLAB实现多变量高斯过程回归(GPR)
开发语言·matlab·回归
ytttr8738 小时前
隐马尔可夫模型(HMM)MATLAB实现范例
开发语言·算法·matlab
天远Date Lab8 小时前
Python实战:对接天远数据手机号码归属地API,实现精准用户分群与本地化运营
大数据·开发语言·python
listhi5208 小时前
基于Gabor纹理特征与K-means聚类的图像分割(Matlab实现)
开发语言·matlab