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
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...