c/c++ 右值引用

来源:https://www.cnblogs.com/xiaoshiwang/archive/2018/09/03/9581627.html
-Advertisement-
Play Games

c/c++ 右值引用 轉自:https://www.cnblogs.com/catch/p/3500678.html 左值(lvalue)和右值(rvalue)是 c/c++ 中一個比較晦澀基礎的概念,不少寫了很久c/c++的人甚至沒有聽過這個名字,但這個概念到了 c++11 後卻變得十分重要,它們 ...


c/c++ 右值引用

轉自:https://www.cnblogs.com/catch/p/3500678.html

左值(lvalue)和右值(rvalue)是 c/c++ 中一個比較晦澀基礎的概念,不少寫了很久c/c++的人甚至沒有聽過這個名字,但這個概念到了 c++11 後卻變得十分重要,它們是理解 move/forward 等新語義的基礎。

左值右值的定義

左值與右值這兩概念是從 c 中傳承而來的,在 c 中,左值指的是既能夠出現在等號左邊也能出現在等號右邊的變數(或表達式),右值指的則是只能出現在等號右邊的變數(或表達式).

int a;
int b;

a = 3;
b = 4;
a = b;
b = a;

// 以下寫法不合法。
= a;
a+b = 4;

在 c 語言中,通常來說有名字的變數就是左值(如上面例子中的 a, b),而由運算操作(加減乘除,函數調用返回值等)所產生的中間結果(沒有名字)就是右值,如上的 3 + 4, a + b 等。我們暫且可以認為:左值就是在程式中能夠尋值的東西,右值就是沒法取到它的地址的東西(不完全準確),但如上概念到了 c++ 中,就變得稍有不同。具體來說,在 c++ 中,每一個表達式都會產生一個左值,或者右值,相應的,該表達式也就被稱作“左值表達式", "右值表達式"。對於基本數據類型來說(primitive types),左值右值的概念和 c 沒有太多不同,不同的地方在於自定義的類型,而且這種不同比較容易讓人混淆:

1) 對於基礎類型,右值是不可被修改的(non-modifiable),也不可被 const, volatile 所修飾(cv-qualitification ignored)

2) 對於自定義的類型(user-defined types),右值卻允許通過它的成員函數進行修改。

對於 1),這和 c 是一致的,2) 卻是 C++ 中所獨有, 因此,如果你看到 C++ 中如下的寫法,千萬不要驚訝:

#include <iostream>
using namespace std;

class Test{
public:
  Test(int d) : data(d){cout << "create:" << data << endl;}
  ~Test(){cout << "free:" << data << endl;}

  Test& operator = (const Test& other){
    //data = other.data;                                           
    cout << "operator" << endl;
    return *this;
  }

  int get_data() const {return data;}
  void set_data(int d){data = d;}
private:
  int data;
};

Test getTest(){
  int static i = 0;
  return Test(i++);
}

void func(Test& t){
  cout << "func:" << t.get_data() << endl;
}
int main(){
  //1 合法                                                              
  (getTest() = Test(1)).set_data(12);                            
  getTest() = Test(2);                                           
  getTest().set_data(20);                                        

  //2 只能用const引用接收右值引用                                  
  const Test& ref = getTest();                                   
  //Test& ref1 = getTest();//error                                 

  //3                                                              
  //func(getTest());                                               
  func(getTest() = getTest());
}

這個特性看起來多少有些奇怪,因為通常來說,自定義類型應該設計得和內置類型儘量一樣(所謂 value type,value semantic),但允許成員函數改變右值這個特性卻有意無意使得自定義類型特殊化了。對此,我們其實### 可以這樣想,也許會好理解點:

<font color="red"自定義類型允許有成員函數,而通過右值調用成員函數是被允許的,但成員函數有可能不是 const 類型,因此通過調用右值的成員函數,也就可能會修改了該右值,done!

左值引用,右值引用

關於右值,在 c++11 以前有一個十分值得關註的語言的特性:右值能被 const 類型的引用所指向,所以如下代碼是合法的。

const Test& ref = getTest();    

而且準確地說,右值只能被 const 類型的 reference 所指向,非 const 的引用則是非法的:

Test& ref1 = getTest();//error 

當一個右值被 const 引用指向時,它的生命周期就被延長了。其中暗藏的邏輯其實就是:右值不能當成左值使用(但左值可以當成右值使用)。另外值得註意的是,對於前面提到的右值的兩個特性:

1) 允許調用成員函數。

2) 只能被 const reference 指向。

它們導致了一些比較有意思的結果,比如:

void func(Test& c)
{
   cout << "c:" << c.get_data() << endl;
}

//error
func(getTest());

//正確
func(getTest() = getTest());

其中: func(get_cs() = get_cs()); 能夠被正常編譯執行的原因就在於,cs 的成員函數 operator=() 返回的是 cs&!

不允許非 const reference 引用 rvalue 並不是完美的。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 剛從事開發那段時間不習慣輸出日誌,認為那是無用功,徒增代碼量,總認為自己的代碼無懈可擊;老大的叮囑、強調也都視為耳旁風,最終導致的結果是我加班排查問題,花的時間還挺長的,要復現問題、排查問題等,幸虧那是公司內部員工用的系統,時間長一點也沒什麼大問題,但是如果是針對客戶的,時間就代表很多東西了, ...
  • c++ move關鍵字 move的由來:在 c++11 以前存在一個有趣的現象:T& 指向 lvalue (左傳引用), const T& 既可以指向 lvalue 也可以指向 rvalue。但卻沒有一種引用類型,可以限製為只指向 rvalue。 就這麼簡單!你甚至可以暫時想像它的原型是這樣的(當然 ...
  • 定義用戶去銀行存錢,每次存100,存3次 餓漢式 懶漢式 多線程 在同一時間,做多件事情. 創建線程的方法 繼承類Thread並重寫run(),run()稱為線程體;用這種方法定義的類不能再繼承其他類。 class FirstThread extends Thread{ public void ru ...
  • 常量池: 字元串一旦被初始化就不會被改變 這段代碼看上去s的值是被改變了的,實際上123就是一個對象,他存在於常量池中,abc也是一個對象,s的值實際上是指向123或者abc的地址。 所以當我們使用String s="123";來定義字元串的時候,會先查看常量池中是否有123,有就直接賦值123的地 ...
  • 前言 從去年八月末開始工作一年了,有了大半年的java開發經驗,自認為比在大學時候編碼能力強了很多,但是基礎方面叫不准的地方感覺越來越多了 (;´д`)ゞ 所以,我準備把這些問題以及工作中遇到的問題總結,記錄下來,造福自己和大家~ヾ(o・ω・)ノ 當然,如果大家發現我哪裡寫的有錯誤,歡迎在下方評論指 ...
  • 在Python3.6.5版本測試通過 語法 str.format(*args, **kwargs) 它通過{}和:來代替%。 "映射”示例: 1.通過位置 In [1]: '{0},{1}'.format('kzc',18) Out[1]: 'kzc,18' In [2]: '{},{}'.form ...
  • 一.基礎配置 1.引入依賴 2.創建主類,通過 @EnableFeginClients 註解開啟 Feign 功能 3.定義AService介面,通過 @FeignClient 註解指定服務名來綁定服務, 然後使用SpringMVC 的註解來綁定具體該服務提供的 REST 介面 需要調用 AServ ...
  • 對文件操作流程如下: 1、打開文件,得到一個文件句柄並賦給一個變數 2、通過文件句柄對文件進行操作 3、關閉文件 4、為了必免打開文件忘記關閉,可以通過上下文管理,即: with open("F:\yesterday2","r",encoding="utf-8") as f: for line in ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...