Practical usage of cpp reference and move semantic

来源:https://www.cnblogs.com/psklf/archive/2022/06/14/16375466.html
-Advertisement-
Play Games

Practical usage of cpp reference and move semantic 在優化重構一部分老代碼時,實際使用 c++ 的 reference 與 move semantic 遇到了若幹問題,在此記錄。 Aggregation 首先,數據的設計並不複雜,只有一個類,成員變數 ...


Practical usage of cpp reference and move semantic

在優化重構一部分老代碼時,實際使用 c++ 的 reference 與 move semantic 遇到了若幹問題,在此記錄。

Aggregation

首先,數據的設計並不複雜,只有一個類,成員變數為一個 std function 並需要在初始化時賦值。最初設計如下,
我希望盡一切可能避免保存 function 對象的副本,所以將函數參數與成員變數全部用 reference 表示。

class UniformValueWrapper {
  public:
    explicit UniformValueWrapper(const std::function<UniformValue(const JsonishValue *)> &parse_func) :
      parse_func_(parse_func) {}
    const std::function<UniformValue(const JsonishValue *)> &parse_func_;
};

// using
UniformValueWrapper wrapper([](const JsonishValue *jsonish) {
   return UniformValue{jsonish->toJsonBool()->getBool()};
   }
   );

這樣的寫法編譯鏈接無誤,實際運行時,卡在實際真正調用這個函數的地方了。當然各個平臺由於編譯器和操作系統不
同可能有不同的表現,我相信在某些平臺我的代碼應該是直接 crash 的。那麼問題出在哪裡?我的預想是在構造函數中
傳遞 const ref 使其擴展局部變數(即提供的這個函數)的生命周期,使其可以保存在 UniformValueWrapper 這個
類的生命周期中。但是實際情況是,局部變數的生命周期只能維持到構造函數調用完成,所以在UniformValueWrapper
中的 ref member 雖然保存了原有的 reference 但是對應的實例在構造函數調用完成後就已經釋放。

實際上在 class member variable 使用 reference 的做法是 OOP 中的 aggregation.

In UML it is called aggregation. It differs from composition in that the member object is not owned by the referring class.
The main reason for aggregation is that the contained object is not owned by the containing object and thus their lifetimes are not bound. In particular the referenced object lifetime must outlive the referring one. It might have been created much earlier and might live beyond the end of the lifetime of the container.

所以生命周期問題是所有使用 aggregation 方式時需要時刻記在心裡的關鍵點。

針對我的使用場景,修改方法其實很簡單,就是放棄使用 aggregation 把 class member 的 reference 去掉。

modernize-pass-by-value

據上一章,我作如下修改:

class UniformValueWrapper {
   public:
     explicit UniformValueWrapper(const std::function<UniformValue(const JsonishValue *)> &parse_func) :
       parse_func_(parse_func) {}
     std::function<UniformValue(const JsonishValue *)> parse_func_;
 };

這樣寫法沒有問題,但是 clang 編譯器提示 "Clang-Tidy: Pass by value and use std::move" 這裡很有意思了,
為什麼我所認為的使用 const reference 的寫法明明是高效傳遞變數,避免不必要的 copy 操作,為何讓我改用低效的
pass by value? 查了一下 Clang Tidy 的文檔,初看起來,兩種方式的區別在於 copy 發生的時機。

  1. 使用 const ref 的方式,在構造函數參數傳遞沒有 copy 但是在 parse_func_(parse_func) 這裡進行了 copy
  2. 使用 pass by value 方式,在構造函數參數傳遞時進行 copy 但是後面的 member initialize 改成了 parse_func_(std::move(parse_func))

那麼,實際上一次 copy 是無法避免的,只是在哪個時機發生而已,為什麼要求我們使用後一種方式呢?

這裡現代 C++ 編譯器有一個牛逼的優化,叫做 copy elision. 在一個聲明瞭 value 參數的函數中,當函數實際參數
是 rvalue 時,編譯器能判斷出多餘的 copy 操作,並主動忽略之,則在函數內直接使用了實參對象。這樣的做法和
const ref 似乎是一致的。其實 Return Value Optimization 也是 copy elision 的一個體現。那麼回到上面的問題,
當時機合適的時候,其實一次 copy 都沒有發生,直接就把函數參數 move 到了最終的地方。如果時機不合適,那麼
最差也就是和原來一樣,進行了一次 copy 操作。這就是為什麼推薦這樣寫的原因,編碼者不用費心去思考這個函數
實際調用時的情況,讓 Clang 來幫我們做判斷,我們這樣寫能保證最高效情況能出現,且最差情況也不會使其差過
原本的寫法。

摘錄一下黃金準則:

Guideline: Don’t copy your function arguments. Instead, pass them by value and let the compiler do the copying.

最終的代碼

class UniformValueWrapper {
 public:
  explicit UniformValueWrapper(std::function<UniformValue(const JsonishValue *)> parse_func) :
  parse_func_(std::move(parse_func)) {}
  std::function<UniformValue(const JsonishValue *)> parse_func_;
};

Ref:

  1. https://clang.llvm.org/extra/clang-tidy/checks/modernize-pass-by-value.html
  2. Want Speed? Pass by Value.

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

-Advertisement-
Play Games
更多相關文章
  • 在我們進行前端開發時,針對項目優化,常會提到一條:針對較小圖片,合理使用Base64字元串替換內嵌,可以減少頁面http請求。 並且還會特別強調下,必須是小圖片,大小不要超過多少KB,等等。 那麼,Base64又到底是什麼呢? 初步認識 下麵的這段字元串,應該是大家都很常見的。通過這種固定的格式,來 ...
  • Install 1. git clone https://github.com/snail-boy/snail-player-native.git 2. 拷貝lib目錄下的文件到自己項目里 Usage 直接運行index.html <!DOCTYPE html> <html lang="en"> < ...
  • 廣義的開放平臺是個龐大的結構,它站在核心業務系統的前面,承接著所有的流量。公司所有的客戶端比如Web站點、手機APP、智能硬體都對接開放平臺API,只是各自的許可權不同,可以訪問的資源不同。狹義的開放平臺只是打開了一扇門,讓合作伙伴進來參與業務互動。從業務層面上看,開放平臺屬於流量渠道之一。本文重點討... ...
  • 0. 前情提要 面試官: 你能手寫個LRU緩存嗎? 你: LRU是什麼東西?(一臉懵逼狀) 面試官: LRU全稱Least Recently Used(最近最少使用),用來淘汰不常用數據,保留熱點數據。 你寫了5分鐘,然而只寫了個get和put方法體,裡面邏輯實在不知道咋寫。 面試官: 今天的面試先 ...
  • 順序執行 C 語言的程式是順序執行,即先執行前面的語句,再執行後面的語句。 條件執行 if if語句用於條件判斷,滿足條件時,就執行指定的語句。 if (expression) { statement // 表達式expression為真(非 0 值)時,就執行 statement 語句。 } if ...
  • @ 一.前言 使用PyQt5模仿網易雲音樂,只有UI沒有功能。 二.展示-主界面 1.靜圖1 主界面 2.靜圖2 主界面-歌單 3.靜圖3 主界面-播客 3.靜圖3 主界面-最新音樂 4.動圖1 主界面-綜合動圖展示 三.展示-登錄界面 仿照製作了一個登錄頁面(二維碼是可以掃描的,並且具有時效性哦~ ...
  • C++預設參數及其本質 1. 概述 概述 C++ 允許函數設置預設參數,在調用時可以根據情況省略實參 其規則如下 預設參數只能按照從右到左的順序。 如果函數同時有聲明、實現、預設參數只能放在函數聲明中 預設參數的值可以是常量、全局符號(全局變數、函數名) 實例1:預設參數只能按照從右到左的順序、預設 ...
  • blind watermark 盲水印技術實現,這裡主要引用網上三種java實現的效果測試和研究。可以將文字隱藏在圖片中,通過提取還原水印,實現版本保護效果。 開源代碼: https://gitee.com/chejiangyi/shuiyin/tree/master ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...