甘子萱 发表于 2026-1-13 21:35:28

【C++】移动语义和完美转发

前言

学习C++移动语义和完美转发笔记,记录左值、右值、std::move()、万能引用、引用折叠等相关内容。
概念


[*]左值 (lvalue) 它是在内存中有明确存储地址、可以被寻址的值。如果你可以对一个表达式取地址(使用 & 运算符),那么它就是一个左值。左值通常是持久的,在它所在的定义域结束之前一直存在
[*]左值引用(Lvalue Reference)本质上就是给一个现有的左值起了一个“别名”,左值引用定义即初始化。

[*]普通左值引用 (T&):只能绑定到非 const 左值。
[*]常量左值引用 (const T&):可以绑定到一切(左值、const 左值、右值)。

const int& temp = 10;
//编译器会在内存中产生一个临时变量存储 10。
//temp 绑定到这个临时变量上。
//这个临时变量的寿命会变得和引用 temp 一样长。

[*]右值 (rvalue) 右值就是那些临时出现、没有持久名字、无法取地址的值。如果你无法对一个表达式使用 & 取地址运算符,或者它是一个即将销毁的临时对象,它就是右值。右值通常是“瞬时”的,在包含它的表达式执行完之后,它就会被立即销毁.
[*]右值引用(Rvalue Reference)一种绑定到右值(临时对象)的引用类型。
int&& rref = 10;

[*]std::move 并不移动任何东西。它的唯一作用是:强制将一个左值转为右值引用。
template <typename T>
typename std::remove_reference<T>::type&& move(T&& t) {   
    return static_cast<typename std::remove_reference<T>::type&&>(t);
}

[*]std::remove_reference::type:这是一个类型萃取工具。无论 T 是 int、int& 还是 int&&,它都能把修饰符去掉,只留下纯粹的底层类型 int。
[*]static_cast 将输入变量 t 强制转换为该类型的右值引用(即 type&&)

[*]std::forward 如果原始参数是左值,转发后仍然是左值。如果原始参数是右值,转发后仍然是右值。
template <typename T>
T&& forward(typename std::remove_reference<T>::type& t) noexcept {
    return static_cast<T&&>(t);
}

[*]移动语义(Move Semantics) 本质是资源所有权的转移,即将一个临时对象(右值)持有的资源转移,来避免昂贵的深拷贝操作。是由类实现的功能(通过移动构造函数)
[*]完美转发(Perfect Forwarding) 是:在函数模板中,将参数原封不动地转发给另一个函数,同时完全保留参数的所有属性(包括它是左值还是右值、是否带有 const 或 volatile 修饰符)。
template <typename T>
T&& forward(typename std::remove_reference<T>::type& t) noexcept {
    return static_cast<T&&>(t);
}

[*]如果原始参数是左值,转发后仍然是左值。
[*]如果原始参数是右值,转发后仍然是右值。

[*]万能引用

[*]使用 && 符号
[*]必须发生模板类型推导:通常出现在 template之后的 T&&(不能加 const)。
[*]形式必须完全匹配:必须是具体的 T&&,不能有 const 或 std::vector&& 等修饰。
[*]万能引用是完美转发的“门”。 它把参数原封不动地领进来(无论是左值还是右值),然后配合 std::forward 把它原封不动地送出去。

[*]引用折叠是 C++ 编译器在处理“引用的引用”时遵循的一套自动简化规则。只要有左值引用(&)参与,结果就是左值引用;只有全是右值引用(&&)时,结果才是右值引用。
内容

左值

内存中有明确存储地址、可以被寻址的值
int a = 10;      // a 是左值(有名字,可取地址)
a = 20;            // a 在左边,OK

int* p = &a;       // p 是左值
*p = 30;         // *p(解引用结果)是左值

int** pp = &(*p);// 我们可以对 (*p) 再次取地址 ,可以看到*p是可以取地址的

const int b = 5;   // b 是左值(虽然不可修改,但它有内存地址,是具名变量)

void func(int&& x) {
    // 这里的 x 类型是右值引用,但 x 本身是一个有名字的变量
    // 所以在函数内部,x 是一个左值!
    int* p = &x; // 这是合法的
}左值引用

对左值的引用,即给左值起别名,必须初始化。
int a = 10;int& ref = a;// ref 是 a 的左值引用const int& r3 = 10; // 正确!std::cout

向梦桐 发表于 2026-1-15 23:22:11

感谢,下载保存了

赵淳美 发表于 2026-1-19 09:05:37

感谢发布原创作品,程序园因你更精彩

骆贵 发表于 2026-1-20 01:41:33

谢谢分享,辛苦了

阎一禾 发表于 2026-1-20 21:20:47

懂技术并乐意极积无私分享的人越来越少。珍惜

娥搽裙 发表于 2026-1-21 14:23:04

用心讨论,共获提升!

榕闹 发表于 2026-1-22 01:35:28

感谢分享,下载保存了,貌似很强大

常士 发表于 2026-1-22 20:29:58

yyds。多谢分享

尝琨 发表于 2026-1-23 01:50:56

感谢,下载保存了

处匈跑 发表于 2026-1-24 05:19:40

感谢分享,下载保存了,貌似很强大

钿稳铆 发表于 2026-1-27 05:11:17

感谢分享

左丘平莹 发表于 2026-1-27 06:03:28

谢谢楼主提供!

郏琼芳 发表于 2026-1-30 04:59:40

yyds。多谢分享

麓吆 发表于 2026-2-4 11:00:08

用心讨论,共获提升!

咪四 发表于 2026-2-10 07:59:59

谢谢分享,试用一下

撵延兵 发表于 2026-2-11 06:31:40

谢谢分享,辛苦了

倡遍竽 发表于 2026-2-11 12:37:49

用心讨论,共获提升!

剩鹄逅 发表于 2026-2-12 22:06:53

东西不错很实用谢谢分享

滤冽 发表于 2026-2-13 07:15:17

前排留名,哈哈哈

臧莞然 发表于 2026-2-24 04:50:04

谢谢楼主提供!
页: [1] 2
查看完整版本: 【C++】移动语义和完美转发