C++ 的 attribute(參考)是在 C++11 新加入的東西。他基本上算是在程式碼裡面,加上特別的輔助說明,給編譯器看,讓編譯器可以在編譯時針對這些屬性來做處理。
這樣的功能其實以前編譯器大多是有自己定義。像 gcc 是用
,現在則算是終於有個統一的標準了。__attribute__
(參考)、MSVC 則是有 __declspec(參考)
目前 C++ attribute 的寫法滿特別的,他是直接用兩組中括號夾住、寫成 [[…]] 這樣的形式。而到 C++20 為止,也訂了超過十個 attribute 了(到還沒定案的 C++20)(除了標準定義的部分,編譯器也還有可以有自己定義的東西);在大部分的狀況下,他們主要的功能大概就是避免編譯器產生不必要的警告、增加程式碼的可讀性,以及讓編譯器更好最佳化吧~
其中有的個人覺得還算滿有用的,所以這邊就大概紀錄一下。
[[nodiscard]] (C++17、參考)
這個 attribute 是用在函式上,他的的目的是用來告訴編譯器(以及開發者),這個函式的回傳值不應該被忽略。
一般在呼叫函式的時候,有的時候會覺得函式的回傳值沒有用,所以就不去接了。但是實際上,有的時候這些還傳值可能是在使用上有必要的。
比如說,一個函式可能在內部用 new 配置了一塊新的記憶體空間,然後希望之後由外部來控管這塊記憶體空間,所以把它的指標回傳出來(雖然一般不建議這樣寫),那如果使用這個函式的人沒有去接這個指標、並做後續處理的話,這裡就會造成 memory leak 了!
比如說下面的程式就是一個簡單的例子(不良寫法,請勿模仿):
#include <iostream> int* generate(int iSize) { return new int[iSize]; } int main() { generate(10); }
而實際上,這樣的程式在編譯的時候,既不會產生編譯錯誤、也不會有任何警告,開發者可能會不小心犯錯。
如果加上 C++17 的 [[nodiscard]] 的話,則可以寫成:
[[nodiscard]] int* generate(int iSize) { return new int[iSize]; }
如此一來,在編譯的時候,編譯器就會因為沒有去接這個回傳值,而給一個警告(MSVC 給的)
warning C4834: 正在捨棄具有 ‘nodiscard’ 屬性之函式的傳回值
提醒開發者他不應該跳過這個回傳值,進而減少出問題的可能性。
而除了這種極端的記憶體配置的問題外,其實 [[nodiscard]] 也可以用來提醒使用函式的人,一定要去檢查回傳值(比如說是否執行成功)。
所以,如果對於有再檢查編譯器警告的人來說,這個 attribute 也是一個可以避免錯誤的東西。
這個 attribute 也是一樣用在函式上的,它的用處是告訴編譯器,這個函式不會回到呼叫者這邊;也就是之後的東西不會被執行到,一般來說應該會用在強制中斷程式、或是丟出例外的時候。(不是指沒有回傳值)
透過加上這個 attribute,在某些情況下,也可以避免編譯器產生相關的警告。
比如下面的程式:
void fatal_error() { throw "error"; } int sw( int v) { switch (v) { case 1: return 0; default: fatal_error(); } }
這樣的程式,編譯器會因為沒有全部的可能性都有回傳值,所以出現下面的警告:
warning C4715: ‘sw’: 不是所有控制路徑都傳回值
但是如果把 fatal_error() 改成:
[[noreturn]] void fatal_error() { throw "error"; }
這樣就不會出現警告了!
而同時,開發者看到 [[noreturn]] 的時候,也可以馬上理解到呼叫這個函式後,不會記繼續執行後面的東西,也算是一個幫助程式碼閱讀的語法了。
[[deprecated]] (C++14、參考)
告訴編譯器和開發者,這東西已經被廢棄了。一般來說,加上這個標記是代表他雖然還可以用,但是最好不要用;比較常見的狀況,似乎大部分是改版後,預計之後會整個抽掉的狀況。
而和前面兩個 attribute 只能用在函式不同,[[deprecated]] 也可以用在變數等東西上。
比如說如果有一個函式是:
[[deprecated]] void test() { //... }
那在沒有呼叫他的情況下,都不會有問題,不過如果有呼叫的話,就會出現下面的錯誤:
error C4996: ‘test’: 宣告為已被取代
由於在 MSVC 是直接將他視為錯誤,所以如果要繼續建置,就需要自行把這個錯誤關閉;比如說再呼叫的時候,寫成:
#pragma warning(suppress : 4996) test();
就可以避免因為這個問題,而導致整個專案無法建置。
而如果數量太多不想一個一個解的話,也可以直接將整個專案的這個錯誤都關閉,這部分可以參考 MSDN 的《Compiler Warning (level 3) C4996》一文。
另外,[[deprecated]] 也可以加上說明文字,告訴開發者為什麼他被廢棄了。像如果寫成:
[[deprecated("Will remove in next release")]] void test() { //... }
那編譯器的錯誤訊息就會變成
error C4996: ‘test’: Will remove in next release
這樣開發者就不用去找資料,馬上就知道原因了~
這篇大概就這樣了。整體來說,這些 attribute 基本上只能算是輔助用的東西,本身可以說是完全不影響程式了~不過如果妥善使用的話,他們應該是可以讓程式碼更好閱讀、更不容易出錯的東西了。
其他,其實也還有像是 [[maybe_unused]] (C++17、參考)、[[fallthrough]] (C++17、參考、參考)這些 C++17 的 attribute,不過 Heresy 這邊測試是發現 MSVC 好像本來就沒有吐相關的警告?所以這邊就先跳過吧。
而 C++20 也又加入了了好幾個新的 attribute,不過這邊也是先不管了。
其他參考:《C++17: Attributes》