前往
大廳
主題

C++雜記-LValue, RValue

最後的疼愛是腿張開 | 2023-01-05 21:42:56 | 巴幣 3144 | 人氣 523

0. 前言

最近工作稍微告一個段落
想說沒事就來研究C++
就順便把研究的心得也記錄起來
我想這應該會是一個非常長的系列
主要除了想記錄下自己的研究心得
也讓我能在多年後回首自己成長的軌跡
至於先前的資料結構系列我應該也會慢慢來補...

1. 正文

最近在研究RValue Reference / LValue Reference / Universal Reference
在開始介紹這三者前
我想先寫一點LValue / RValue的介紹
中文分別稱為左值 / 右值
比較簡單的理解LValue就是那些能出現=左右兩邊的變數
像是你所創造的變數
例如:int i; / Class Student; / std::string s;等等
而RValue只能出現在=右側
像是那些常量 / function return的值(有例外 下面會說)
例如:123 / "76ers is the championship of 2023 NBA's Final." / 0.23等等
精確一點來講
RValue就是在程式運行中並不會對其分配特定記憶體的東西
可能僅存於暫存器中
所以簡單區分LValue, RValue的方式就是對他作取址的動作
如果這運算式可行即是LValue

講完LValue與RValue的簡單介紹
來講一個算冷知識的東西
相信有些人聽過Built-in prefix operator會比Built-in postfix operator更有效率一點
Built-in prefix operator就是大家都用過的++i / --i
Built-in postfix operator為i++ / i--
要來講這兩者為什麼有效能差異
就得從這兩個operator Prototype開始講了
下面都以increment做舉例

先來看prefix operator
class內部的宣告
T& T::operator++();
class外部的宣告
T& operator++(T& a);

再來是postfix operator
class內部的宣告
T T::operator++(int);
class外部的宣告
T operator++(T& a, int);

這兩者一對比很容易就能發現不同的地方
prefix operator回傳的是reference而postfix回傳的是物件
且postfix的參數多了一個int
第一個不同就牽扯到效能的影響
晚點實作時會一併說明
而第二個不同參數多了一個int
這麼做是為了區分要呼叫prefix operator還是postfix operator
所以通常值都為0
也因此這個int亦被稱為dummy parameter
但C++支援operator overload想不加0也可以自己overload

接下來我會分class內部與外部分別實作

class內部:
class外部:

看完這兩個的實作可以來解答一個我知道結果但不知道為什麼的現象了
前者會印出1後者則會印出2
原因就在於postfix operator會用一個臨時變數保存初始的變數值
然後才執行+1
最後回傳原本的變數
所以才會得到1
而prefix operator的行為則是對reference做+1後回傳更動後的值
因此才會得到2

解決完這個從我開始寫程式就沒仔細想過的小問題
現在可以來說為什麼prefix跟postfix會有效能上的差異了
原因就在於postfix會創造一個臨時變量
不管是用copy constructor或是要重載=的方式都免不了有複製的動作
也因此如果對於較為肥大or自訂義的變數來說會影響到效能。
也因此才會建議在for loop盡量都以prefix operator來做迭代
因為這兩者就結果來說並無差異

2. 總結

結果冷知識的篇幅反而比較多
得稍微把LValue, RValue來跟它做一些連結
順便解答一下在RValue舉例我說function return的值為RValue但有例外
例外就是 當function回傳的是reference時 它會變LValue
很神奇吧
原因就在於C++對reference設計的精妙之處了
資工系 / 電機系的一定寫過C
多半會同意pointer與function結合有多痛苦吧
(望向DSA的作業...各種parameter的*比我當兵遇到的長官還要多)
扯遠了
C++引入了reference的概念
實際上它依然是一種特殊的指標
只是沒一般指標來的靈活
詳細差異這邊就不做展開介紹 因為感覺這也是能寫一篇來討論
就留到之後來寫吧

回到上面說的當function return的是reference會是LVaule
精準一點來說
這種宣告本來就是return LValue Reference的意思
也因此我們能將這種return的LValue Reference擺在=左邊
講完上面這些
我們可以來看一小段程式碼了
如果把這一小段程式碼丟去編譯
後者一定會跳不可以對RValue取址
而前者卻沒事
至於原因結合冷知識與後面做的function return LValue reference講解應該就能得到解答了

3. 延伸

我想平常應該不太會有人使用到return LValue Reference
但Design Pattern中有個Singleton Pattern就是這樣實作得
感覺Design Pattern也是可以寫好幾篇的介紹
之後有時間會慢慢來寫得
就簡單寫一小段讓大家看看C++可以寫得多酷

原本想說要再一口氣把RValue Reference / LValue Reference / Universal Reference給講完
但篇幅已經有點長了
我下一篇再繼續接著寫吧

此篇主要為研究心得
資料多蒐集自網路後 由本人的理解所饌而得
如有疏漏錯誤 尚祈各位不吝指教 感謝><



創作回應

哇啦哇哈哈
推老師 我一輩子的超人
2023-01-06 13:16:25
最後的疼愛是腿張開
我等您Google總部下午茶食記><
2023-01-06 17:53:59
呆呆獸
老師...真的好強
2023-01-06 13:30:41
最後的疼愛是腿張開
抱歉 我不知道你誰XD 但還是感謝你ㄉ收看 耶
2023-01-06 17:54:25
章魚茶
嗯……那回傳const LValue應該就不會變成RValue?感謝幫忙整理
2023-01-07 14:53:14
最後的疼愛是腿張開
也有可能我沒找到 如果有相關的資料再麻煩您提供給我學習一下>< 到時候會一併更新上去ㄉ
2023-01-07 18:17:53
章魚茶
https://stackoverflow.com/questions/21778045/c-return-value-reference-const-reference
後來查了一下,上面這篇如果正確的話,回傳const LValue參照是LValue參照,只是不能動(同時需要避免改動和複製回傳值內容時可用)
C++的標準裡也列了「函數回傳的LValue參照」是LValue,加const應該不會影響回傳值是LValue這件事
2023-01-07 19:09:57
最後的疼愛是腿張開
感謝您也花時間查了一下XD C++真的是越來越難學的語言了...
2023-01-07 23:31:31
章魚茶
C++的東西太多了,外加很多為了C而留存的歷史共業,感覺永遠學不完
這樣跟神燈精靈許願「死前能學完C++」是不是可以永生啊(大誤)
2023-01-07 23:35:10
最後的疼愛是腿張開
家祭無忘告乃翁
2023-01-08 00:18:33

更多創作