背竽
2026-1-13 21:35:28
前言
学习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; // 这是合法的
- }
复制代码 左值引用
对左值的引用,即给左值起别名,必须初始化。
[code]int a = 10;int& ref = a; // ref 是 a 的左值引用const int& r3 = 10; // 正确!std::cout |
|
|
|
|
|
相关推荐
|
|
|