深入剖析C++的 “屬性“(Attribute specifier sequence)

引言

在閱讀開源項目源代碼是,發現了一個有趣且特殊的C++特性:屬性。

屬性(attribute specifier sequences)是在C++11標準引入的。在C++11之前,編譯器特有的擴展被廣泛用來提供額外的代碼信息。例如,GNU編譯器(GCC)使用__attribute__來控制函數的行為。但是缺點也很明顯,那就是這種方式缺乏標準化,不同編譯器的特性并不兼容,嚴重影響了代碼的可移植性。

為了解決這一問題,C++11標準引入了屬性,提供一種標準化的方法來給編譯器額外的信息。屬性既可以應用于類型聲明,也可以應用于聲明語句、定義、代碼塊等,為編譯器提供了關于編碼意圖的附加信息。

屬性的作用

屬性引入C++后,提供了以下幾個方面的價值:

  1. 可移植性:提供了一種標準的方式來傳達編譯器特定的信息,減少了依賴特定編譯器擴展的需要。

  2. 可讀性:通過標準的屬性,代碼的特定行為和意圖被清晰地表達,易于他人理解。

  3. 健壯性:屬性可以幫助檢測潛在的錯誤,如忽略了重要的函數返回值。

  4. 性能:許多屬性可以幫助編譯器進行優化,提高程序性能。

常見屬性

以下是C++中一些常用的屬性:

  1. [[noreturn]] - 表明函數不會返回到調用者。這通常用于那些通過拋出異常或終止程序來退出的函數。

  2. [[nodiscard]] - 用于函數或類型,標明調用者不應忽略返回值。對于類型,它表示該類型的對象或其構造函數返回的對象不應被忽略。

  3. [[deprecated]] - 標記某個實體(如函數、類、類型別名、變量等)為過時的,建議不要使用。可選地,可以提供一條消息說明替代的使用方式。

  4. [[fallthrough]] - 在C++17中引入,用于switch語句的case節中,明確表示允許控制流從當前case分支無條件跳轉到下一個case分支的行為,避免編譯器生成可能的警告。

  5. [[maybe_unused]] - 表示某個實體(如函數、類、變量等)可能不會被使用,從而防止編譯器發出未使用警告。

  6. [[likely]][[unlikely]] - 這兩個屬性是在C++20中引入的,用來顯式地指示給定的布爾表達式結果的可能性。[[likely]]表示表達式結果為true的可能性更高,而[[unlikely]]表示結果為false的可能性更高。

  7. [[no_unique_address]] - 在C++20中引入,它指示類成員不必擁有唯一的地址,允許空基優化或類似的優化技術。

這些屬性可以用來改善代碼的文檔化、提高性能、幫助編譯器檢查錯誤和警告,以及啟用或禁用特定的編譯器行為。
它們是現代C++編程中代碼質量和維護方面的重要工具。不同的編譯器可能對屬性的支持程度有所不同。

常用屬性示例

[[noreturn]]

[[noreturn]]屬性指示某個函數不會返回到調用者。這通常用于那些通過拋出異常或終止程序來退出的函數。

[[noreturn]] void terminate_program() {// ... 清理操作 ...std::exit(1); // std::exit不返回
}

[[nodiscard]]

[[nodiscard]]屬性用于函數或類型,標明調用者不應忽略返回值。這有助于防止編碼中的錯誤,例如忘記處理函數返回的錯誤代碼。

[[nodiscard]] int compute() {// ... 計算操作 ...return result;
}void example() {compute(); // 如果忽略了結果,編譯器可能會警告
}

C++20對nodiscard進行了擴展,支持注明原因。格式為:[[nodiscard(“reason”)]]

[[deprecated]]

[[deprecated]]屬性用于標記過時的實體。

[[deprecated("Use new_function instead")]]
void old_function() {// ...
}void example() {old_function(); // 使用這個函數時,編譯器會給出警告
}

[[fallthrough]]

[[fallthrough]]屬性用在switch語句中,表示有意識的case穿透。

void example(int val) {switch (val) {case 1:// 計算某些事情[[fallthrough]]; // 明確表明穿透是有意為之case 2:// 1和2執行相同的代碼break;default:// 其他值處理break;}
}

[[maybe_unused]]

[[maybe_unused]]屬性用于可能不使用的變量或函數,防止編譯器發出未使用警告。

[[maybe_unused]] static bool is_debug = true;void example() {[[maybe_unused]] int local_variable = compute();
}

[[likely]] 和 [[unlikely]]

[[likely]][[unlikely]]在C++20中引入,幫助編譯器優化分支預測。

bool condition = /* ... */;
if ([[likely]] condition) {// 大多數情況下,條件為真時的代碼
} else {// 條件為假時的代碼
}

其他不常用的屬性介紹

[[carries_dependency]] (C++11)

[[carries_dependency]]屬性用來指示在函數參數或返回值中的依賴關系鏈可以傳遞,這在使用std::memory_order時或在多線程編程中非常重要。

#include <atomic>
#include <iostream>std::atomic<int> global_data;// 該函數表明參數和返回值帶有依賴關系鏈
[[carries_dependency]] int load_data(std::atomic<int>& data) {return data.load(std::memory_order_consume);
}void process_data() {// 從全局數據載入的依賴關系被保留int local_data = load_data(global_data);// 接下來的操作可以依賴于這個順序
}

[[no_unique_address]] (C++20)

[[no_unique_address]]屬性表示非靜態數據成員不必具有唯一的地址,這允許編譯器在可能的情況下進行內存優化。在C++中,即使是完全空的類(不含任何成員變量或成員函數)也至少會占用1字節的大小,這是為了確保每個對象都有一個唯一的地址。但是,有時候這個額外的1字節并不是必須的,例如當空類作為其他類的成員時,這在包含多個空類成員的大型結構體中可以節省大量內存。

struct Empty {}; // empty classstruct X
{int i;Empty e;
};struct Y
{int i;[[no_unique_address]] Empty e;
};int main()
{// the size of any object of empty class type is at least 1static_assert(sizeof(Empty) >= 1); // at least one more byte is needed to give e a unique addressstatic_assert(sizeof(X) >= sizeof(int) + 1); static_assert(sizeof(Y) == sizeof(int)); 
}

[[assume(expression)]] (C++23)

[[assume(expression)]]屬性告訴編譯器,在給定的點上,表達式總是評估為真。這有助于編譯器進行優化。

void process_data(int* ptr) {// 告訴編譯器ptr不為nullptr,這有助于優化[[assume(ptr != nullptr)]]*ptr = 42;
}

[[indeterminate]] (C++26)

[[indeterminate]]屬性是一個假設的屬性,用來指明一個對象如果沒有被初始化,則具有不確定的值。由于C++26還目前還未發布,暫時用不了。

void f(int); 
void g()
{int x [[indeterminate]]; // indeterminate valueint y;                   // erroneous valuef(x); // 允許編譯通過,但是具有不確定的行為f(y); // 編譯報錯,不允許使用未初始化的值
}

[[optimize_for_synchronized]] (TM TS)

[[optimize_for_synchronized]]屬性來自事務性內存技術規范(TM TS),它建議函數應該為同步塊中的調用進行優化。TM TS不是ISO C++標準的一部分,支持度因編譯器而異。

// 這個屬性建議函數在同步塊中調用時應該進行優化
[[optimize_for_synchronized]] void synchronized_function() {// 在同步語句中頻繁調用的函數
}

結語

筆者最喜歡的C++屬性就是[[nodiscard]]了,計劃今天就在團隊中推廣開。因為許多開發者在調用一些可能失敗的函數不檢查返回值,導致代碼魯棒性較低。給一些重要函數加上[[nodiscard]]屬性之后,編譯器就能避免這種情況的發生,真是太有用了。試了一下,MSVC下nodiscard會出現警告,還好我們開了警告視為錯誤,那么就可以確保開發者不會忘記處理返回值了。
在這里插入圖片描述

如果向了解更多關于C++屬性的知識,那么可以來cppreference看看。cppreference的C++的屬性參考:Attribute specifier sequence(since C++11)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/45085.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/45085.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/45085.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AcWing 3587:連通圖 ← dfs(鄰接矩陣 or 鏈式前向星)

【題目來源】https://www.acwing.com/problem/content/3590/【題目描述】 給定一個無向圖和其中的所有邊&#xff0c;判斷這個圖是否所有頂點都是連通的。【輸入格式】 輸入包含若干組數據。 每組數據第一行包含兩個整數 n 和 m&#xff0c;表示無向圖的點和邊數。 接下來 m 行…

Java面試題系列 - 第5天

題目&#xff1a;Java Lambda表達式與Stream API的深度應用 背景說明&#xff1a;Java 8引入了Lambda表達式和Stream API&#xff0c;極大地提升了Java函數式編程的能力&#xff0c;使代碼更簡潔、更易讀。掌握Lambda表達式和Stream API的使用&#xff0c;對于優化數據處理流程…

Qt/C++項目積累: 2.主機監控器 - 2.2 歷史功能實現

修訂歷史&#xff1a; 20240711&#xff1a;初始表設計&#xff0c;采用sqlite 正文&#xff1a; 關于歷史數據存儲&#xff0c;考慮的是用數據庫來完成&#xff0c;目前考慮使用Sqlite和mysql&#xff0c;先用sqlite來實現&#xff0c;設計表過程如下&#xff1a; 機器總覽…

白騎士的C++教學進階篇 2.1 指針與引用

系列目錄 上一篇&#xff1a;白騎士的C教學基礎篇 1.5 數據結構 指針和引用是C中非常重要的概念&#xff0c;它們提供了強大的功能&#xff0c;使程序員能夠直接操作內存&#xff0c;提高程序的效率和靈活性。在本篇博客中&#xff0c;我們將深入探討指針與引用的基礎知識&…

從零開始學習嵌入式----Makefile工具

一、Makefile 簡明指南 你是否曾經面對過一堆源代碼文件&#xff0c;卻不知道如何將它們編譯成可執行文件&#xff1f;或者你是否厭倦了每次修改代碼后都要手動輸入冗長的編譯命令&#xff1f; 如果是的話&#xff0c;那么 Makefile 就是你的救星&#xff01; 二、Makefile 是什…

全網最適合入門的面向對象編程教程:14 類和對象的 Python 實現-類的靜態方法和類方法,你分得清嗎?

全網最適合入門的面向對象編程教程&#xff1a;14 類和對象的 Python 實現-類的靜態方法和類方法&#xff0c;你分得清嗎&#xff1f; 摘要&#xff1a; 本文主要介紹了Python中類和對象中的類方法和靜態方法&#xff0c;以及類方法和靜態方法的定義、特點、應用場景和使用方…

網安防御保護-小實驗

1、DMZ區內的服務器&#xff0c;辦公區僅能在辦公時間內(9:00-18:00)可以訪問&#xff0c;生產區的設備全天可以訪問 2、生產區不允許訪問互聯網&#xff0c;辦公區和游客區允許訪問互聯網 3、辦公區設備10.0.2.10不允許訪問DMZ區的FTP服務器和HTTP服務器&#xff0c;僅能ping通…

vue 環境變量那些事

常見的項目環境變量的使用 總則&#xff1a;使用 .env.環境名字文件去定義&#xff0c;運行命令的時候指定環境 比如&#xff1a;.env 代表所有環境變量使用的一些變量 .env.development 代表的開發環境變量 .env.production 代表的生產環境變量也就是上線以后使用的 vitevu…

自主研發接口測試框架

測試任務&#xff1a;將以前完成的所有的腳本統一改寫為unitest框架方式 1、需求原型 1.1 框架目錄結構 V1.0&#xff1a;一般的設計思路分為配置層、腳本層、數據層、結果層&#xff0c;如下圖所示 V 2.0&#xff1a;加入驅動層testdriver 1.2 框架各層需要完成的工作 1、配…

Fast DDS library windows 下源碼編譯(cmake)

目錄 編譯環境&#xff1a; 編譯需要的源碼文件&#xff1a; Fast DDS編譯&#xff1a; 注意事項&#xff1a; 參考文檔&#xff1a; 基于Fast DDS 的源碼來編譯相關的庫&#xff0c;然后可以通過python 來調用庫文件實現dds 數據通信&#xff0c;本文就詳細的介紹編譯過程…

機器學習筑基篇,容器調用顯卡計算資源,Ubuntu 24.04 快速安裝 NVIDIA Container Toolkit!...

[ 知識是人生的燈塔,只有不斷學習,才能照亮前行的道路 ] Ubuntu 24.04 安裝 NVIDIA Container Toolkit 什么是 NVIDIA Container Toolkit? 描述:NVIDIA Container Toolkit(容器工具包)使用戶能夠構建和運行 GPU 加速的容器,該工具包括一個容器運行時庫和實用程序,用于自動…

石油巨頭受沖擊!埃克森美孚、BP接連發出盈利預警

KlipC報道&#xff1a;近日&#xff0c;BP&#xff08;英國石油&#xff09;預計其第二季度將面臨10億至20億美元的減值費用&#xff0c;并發出警告稱其煉油利潤率“大幅下降”&#xff0c;石油交易收益預計出現疲軟。消息公布后&#xff0c;其股價下跌超4%。 由于中間餾分油利…

JavaScript(8)——函數

函數 function,是被設計執行特定任務的代碼塊。 函數可以把具有相同或相似邏輯的代碼包裹起來&#xff0c;通過函數調用執行這些代碼&#xff0c;這么做的優勢有利于精簡代碼方便復用。類似于alert(),prompt()和console.log()&#xff0c;這些都是js函數&#xff0c;不過已經…

STL(一)

書寫形式&#xff1a;string (const string& str, size_t pos, size_t len npos); 舉例&#xff1a; int main(){ string url("https://mp.csdn.net/mp_blog/creation/editor?spm1000.2115.3001.4503") string sub1(url,0,5);//從下標為0開始向后5個字符&…

如何在 Python 中創建一個類似于 MS 計算器的 GUI 計算器

問題背景 假設我們需要創建一個類似于微軟計算器的 GUI 計算器。這個計算器應該具有以下功能&#xff1a; 能夠顯示第一個輸入的數字。當按下運算符時&#xff0c;輸入框仍顯示第一個數字。當按下第二個數字時&#xff0c;第一個數字被替換。 解決方案 為了解決這個問題&am…

華為OD機考題(HJ108 求最小公倍數)

前言 經過前期的數據結構和算法學習&#xff0c;開始以OD機考題作為練習題&#xff0c;繼續加強下熟練程度。 描述 正整數A和正整數B 的最小公倍數是指 能被A和B整除的最小的正整數值&#xff0c;設計一個算法&#xff0c;求輸入A和B的最小公倍數。 數據范圍&#xff1a;1≤…

Spring中的適配器模式和策略模式

1. 適配器模式的應用 1.1適配器模式&#xff08;Adapter Pattern&#xff09;的原始定義是&#xff1a;將一個類的接口轉換為客戶期望的另一個接口&#xff0c;適配器可以讓不兼容的兩個類一起協同工作。 1.2 AOP中的適配器模式 在Spring的AOP中&#xff0c;使用Advice&#…

【北京迅為】《i.MX8MM嵌入式Linux開發指南》-第一篇 嵌入式Linux入門篇-第十九章 Linux 工具之make 工具和 makefile 文件

i.MX8MM處理器采用了先進的14LPCFinFET工藝&#xff0c;提供更快的速度和更高的電源效率;四核Cortex-A53&#xff0c;單核Cortex-M4&#xff0c;多達五個內核 &#xff0c;主頻高達1.8GHz&#xff0c;2G DDR4內存、8G EMMC存儲。千兆工業級以太網、MIPI-DSI、USB HOST、WIFI/BT…

集群管理腳本

虛擬機集群管理腳本 文章目錄 虛擬機集群管理腳本一、遠程調用腳本(remote_call.sh)二、遠程復制目錄腳本(remote_copy.sh) 一、遠程調用腳本(remote_call.sh) 如果有傳命令參數&#xff0c;則執行該命令&#xff1b;如果沒有傳命令參數&#xff0c;則不執行。 #!/bin/bashcm…

【嵌入式Linux】<知識點> GDB調試(更新中)

文章目錄 前言 一、GDB調試預備工作 二、GDB的啟動與退出 三、GDB中查看源代碼 四、GDB斷點操作 五、GDB調試指令 前言 在專欄【嵌入式Linux】應用開發篇_Linux打工仔的博客中&#xff0c;我們已經寫了大量的源程序。但是在調試這些程序時我們都是通過printf大法和肉眼除…