🦄個人主頁:修修修也
🎏所屬專欄:C++
??操作環境:Visual Studio 2022
目錄
📌左值引用和右值引用
🎏左值和左值引用
🎏右值和右值引用
📌左值引用和右值引用比較?
🎏左值引用
🎏右值引用
📌左值引用和右值引用使用場景和意義
🎏左值引用使用場景和意義
🎏右值引用使用場景和意義
?🎏右值引用引用左值及其使用場景
📌完美轉發
🎏為什么需要完美轉發?
🎏如何實現完美轉發?
?🎏完整示例
結語
📌左值引用和右值引用
??????? 我們在一開始學習c++時就學習過引用的語法,當時我們將引用這一語法理解為給變量起別名。在c++11當中新增了右值引用語法特性,無論是左值引用還是右值引用,都是給對象起別名。注意,要摒棄一個誤區,不能簡單的認為在賦值號左邊的就叫左值,右邊的就叫右值,實際上左值和右值的界定需要參照以下定義:
🎏左值和左值引用
??????? 左值是一個表示數據的表達式(如變量名或解引用的指針),我們可以獲取它的地址,一般可以對它賦值,左值可以出現在賦值符號的左邊,右值不能出現在賦值符號的左邊.定義時const修飾符后的左值,不能給他賦值,但是可以取它的地址,因此還是左值.
??????? 左值引用就是給左值的引用,給左值取別名.如:
//左值引用 int a = 0; int& r1 = a; //給a取別名為r1
🎏右值和右值引用
???????右值是一個表示數據的表達式,如:字面常量, 表達式返回值, 函數返回值(這個不能是左值引用返回)等等,右值可以出現在賦值符號的右邊,但是不能出現在賦值符號的左邊,右值不能取地址.
????????右值引用就是對右值的引用,給右值取別名.如:
//右值引用 int&& r5 = 10; //給10取別名為r5double x = 1.1, y = 2.2; double&& r6 = x + y; //給表達式x+y取別名為r6
📌左值引用和右值引用比較?
🎏左值引用
- 左值引用只能引用左值,不能引用右值
- 但是const左值引用既可以引用左值,也可以引用右值
/左值引用引用右值 double x = 2.2; double y = 3.3; const int& r2 = 10; const double& r3 = x + y;//這里x和y都是左值,但是x+y表達式返回的結果5是一個臨時變量是右值
🎏右值引用
- 右值引用只能引用右值,不能引用左值.
- 但是特殊情況下右值引用可以引用move以后的左值.
//右值引用引用左值 int a = 10; int&& r7 = move(a);
??????? 也就是說,正常情況下左值只能引用左值, 右值只能引用右值, 但是const左值可以引用右值,右值可以引用move后的左值。
📌左值引用和右值引用使用場景和意義
🎏左值引用使用場景和意義
左值引用使用場景:
- 做參數
void swap(int& a,int&b) //左值引用可以直接修改原對象,減少參數傳遞時的拷貝 {int tmp = a;a = b;b = tmp; }int main() {int x = 2;int y = 3;swap(x,y);return 0; }
- 做返回值
//左值引用可以直接修改返回值,同時減少了函數傳值返回的拷貝 int& get(size_t pos) {return data[pos]; }
左值引用意義: 減少拷貝,并可以直接修改原對象
左值引用的缺點:但是當函數返回對象是一個局部變量,出了函數作用域就不存在了,就不能使用左值引用返回,只能傳值返回。例如:
????????函數中可以看到,這里只能使用傳值返回,傳值返回會導致至少1次拷貝構造(如果是一些舊一點的編譯器可能是兩次拷貝構造)。
🎏右值引用使用場景和意義
??????? 通過上面我們對左值引用使用場景和意義的分析,我們得知了左值引用的短板。因此C++的大佬們就引入了右值引用和移動語義來解決這個問題:移動語義包括移動構造和移動賦值,我們先來看移動構造:
??????? 移動構造本質是將參數右值的資源竊取過來,占為已有,那么就不用做深拷貝了,所以它叫做移動構造,就是竊取別人的資源來構造自己:
??????? 而移動賦值也是將賦值運算符右邊的右值資源竊取過來,占為己有,也就不用再做深拷貝了:
??????? 基于上面的概念,實現的string類移動構造和移動賦值函數如下:
//移動構造 string(string&& s):_str(nullptr), _size(0), _capacity(0) {swap(s); }//移動賦值 string& operator=(string&& s) {swap(s);return *this; }
?🎏右值引用引用左值及其使用場景
??????? 有些場景下,我們可能需要用右值去引用左值實現移動語義。當需要用右值引用引用一個左值時,可以通過move函數將左值轉化為右值。
int main() {string s1("hello world");// 這里s1是左值,調用的是拷貝構造string s2(s1);// 這里我們把s1 move處理以后, 會被當成右值,調用移動構造// 但是這里要注意,一般是不要這樣用的,因為我們會發現s1的// 資源被轉移給了s3,s1被置空了。string s3(std::move(s1));return 0; }
📌完美轉發
????????完美轉發(Perfect Forwarding)?是 C++11 引入的核心特性之一,用于在泛型編程中精確傳遞參數的左值/右值屬性,避免不必要的拷貝或類型損失。它結合了?右值引用、萬能引用(Universal Reference)?和?
std::forward
?實現。
🎏為什么需要完美轉發?
????????假設有一個泛型函數?
wrapper
,需要將參數轉發給另一個函數?target
:template<typename T> void wrapper(T arg){target(arg); // 直接傳遞參數 }
問題:
值類別丟失:無論?
arg
?是左值還是右值,target(arg)
?接收的始終是左值(因為右值引用本身是左值, 如果右值引用本身是右值那么就沒法移動語義了)所以左值引用和右值引用傳遞到下層都變成了左值引用。拷貝開銷:若?
arg
?是臨時對象(右值),無法觸發移動語義,可能導致深拷貝。
???????? 右值引用默認是左值,我們才能基于此實現移動語義:
??????? 但是如果不支持完美轉發的話,右值引用無法保持右值屬性,那么我們遇到嵌套容器深拷貝的情況就沒法用移動語義:
🎏如何實現完美轉發?
1. 萬能引用(Universal?Reference)
語法:模板參數中使用?
T&&
,且?T
?需要被推導(如函數模板或?auto
)。特性:可以綁定到左值或右值,保留參數的原始類型信息。
如果實參是左值,他就是左值引用(引用折疊)
如果實參是右值,他就是右值引用
template<typename T> void wrapper(T&& arg) { // arg 是萬能引用// 模板中的&&不代表右值引用,而是萬能引用,其既能接收左值又能接收右值。// 模板的萬能引用只是提供了能夠接收同時接收左值引用和右值引用的能力,// 但是引用類型的唯一作用就是限制了接收的類型,后續使用中都退化成了左值,// 我們希望能夠在傳遞過程中保持它的左值或者右值的屬性, 就需要用我們下面學習的完美轉發 }
2.?
std::forward<T>
作用:根據?
T
?的原始類型(左值或右值),將參數有條件地轉換回原始類型。本質:若?
T
?是左值引用,返回左值;否則返回右值引用(觸發移動語義)。
?🎏完整示例
#include <iostream>
#include <utility> // std::forward// 目標函數
void target(int& x) { std::cout << "左值: " << x << std::endl; }
void target(int&& x) { std::cout << "右值: " << x << std::endl; }// 完美轉發的包裝函數
template<typename T>
void wrapper(T&& arg)
{target(std::forward<T>(arg)); // 關鍵:保留參數的原始類型
}int main()
{int a = 10;wrapper(a); // 傳遞左值 → 調用 target(int&)wrapper(20); // 傳遞右值 → 調用 target(int&&)wrapper(std::move(a)); // 顯式轉為右值 → 調用 target(int&&)return 0;
}
結語
希望這篇關于 C++11之左值引用,右值引用和移動語義 的博客能對大家有所幫助,歡迎大佬們留言或私信與我交流.
學海漫浩浩,我亦苦作舟!關注我,大家一起學習,一起進步!
?相關文章推薦
【C++】STL標準模板庫容器set
【C++】模擬實現二叉搜索(排序)樹
【數據結構】C語言實現鏈式二叉樹(附完整運行代碼)
【數據結構】什么是二叉搜索(排序)樹?
【C++】模擬實現priority_queue(優先級隊列)
【C++】模擬實現queue
【C++】模擬實現stack
【C++】模擬實現list
【C++】模擬實現vector
【C++】標準庫類型vector
【C++】模擬實現string類
【C++】標準庫類型string
【C++】構建第一個C++類:Date類
【C++】類的六大默認成員函數及其特性(萬字詳解)
【C++】什么是類與對象?
?
強烈呼吁大家在寫CSDN的時候把Ctrl+z給禁用掉,否則有一定幾率導致您的創作窗口一秒穿越到n小時前。且根本沒有一點辦法恢復。這篇文章就是血的教訓...🥲