C++ 的一些 attribute

| | 0 Comments| 15:37
Categories:

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 也是一個可以避免錯誤的東西。


[[noreturn]] (C++11、參考參考

這個 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

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *