【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 有条件 转换,保留值类别。

使用场景:

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

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

相关推荐
hansang_IR1 小时前
【记录】四道双指针
c++·算法·贪心·双指针
_OP_CHEN1 小时前
算法基础篇:(十二)基础算法之倍增思想:从快速幂到大数据运算优化
大数据·c++·算法·acm·算法竞赛·倍增思想
Murphy_lx1 小时前
C++ 条件变量
linux·开发语言·c++
xie0510_1 小时前
C++入门
c++
AA陈超1 小时前
ASC学习笔记0027:直接设置属性的基础值,而不会影响当前正在生效的任何修饰符(Modifiers)
c++·笔记·学习·ue5·虚幻引擎
羚羊角uou1 小时前
【C++】智能指针
开发语言·c++
杜子不疼.1 小时前
【C++】哈希表基础:开放定址法 & 什么是哈希冲突?
c++·哈希算法·散列表
代码不停1 小时前
网络原理——初识
开发语言·网络·php
04aaaze1 小时前
C++(C转C++)
c语言·c++·算法