C++的屬性指示符,有點類似Java中Anotation, 是可以實現定義的。它主要是用來引入一些對象,類型,代碼的屬性,也可以理解爲限制對象,類型,代碼的一些行爲。它爲實現定義的語言擴展提供標準統一的語法,比如GNU和IBM的__attribute__((…))
,微軟的__declspec()
語言擴展,等等。
儘管每個特殊的屬性僅當實現允許時纔有效,但是屬性幾乎能在C++程序的任何地方使用,同時也可以被應用到,類型,變量,函數,命名,代碼塊,以及整個的翻譯單元,幾乎所有的實體上。比如,[[likely]]
屬性只能用在if
語句上邊,不能用在函數聲明上;[[omp::parallel()]] 屬性只能用在for
循環語句上,而不能用在類型上面。(這兩個屬性只是爲了舉例方便而虛構的,實際並不存在)
在聲明時,屬性可能出現在實體聲明前後,結合使用的。大多數情況,屬性聲明都放在實體聲明前面,儘管對齊指示符
的語法有些不同,但是它是屬性指示符
的一部分。它可能出現在[[]]
屬性的地方,也可能和他們混合使用(但是隻能用在允許對齊的地方) 兩個連續的左中括號[[
,只能當引入屬性指示符時或者在一個屬性的參數中出現
標準的屬性指示符的格式爲
[[identifier]] (since c++11)
[[ using attribute-namespace::identifier]] (since c++17)
[[identifier(argument-list)]] (since c++11)
[[ using attribute-namespace:identifier(argument-list)]] (since c++17)
注意:
如果 命令空間出現在屬性類表的開頭,那麼屬性列表中的其他屬性不能指定一個命名空間,該屬性
類列表中的所有屬性共享一個命名空間。
標準的屬性指示符
屬性指示符名稱 | 用途 | 引入版本 |
---|---|---|
[[noreturn]] | 表示函數不返回值,並只能應用在函數上面, 如果函數真的有返回值,那麼該屬性無效,並且會給出警告 |
c++11 |
[[carries_dependency]] | 表示消耗釋放模式的依賴鏈在函數內外傳播,允許編譯器跳過一些不必要的柵欄指令 | c++11 |
[[deprecated]] [[deprecated("reason")]] |
表示某些實體已經廢棄,或者因爲某些原因廢棄,可以用在類,定義類型名,變量,非靜態數據成員,枚舉,模板實例 | c++14 |
[[nodiscard]] | 表示被修飾的函數的返回值十分重要,或者枚舉類型,對象類型十分重要不能被廢棄 | c++17 |
[[maybe_unused]] | 抑制一下未使用變量的警告 | c++17 |
[[fallthrough]] | 出現在switch 語句中,抑制上一句case 沒有break 而引起的fallthrough 的警告 |
|
[[noreturn]]
#include <cassert>
[[ noreturn ]] void f() {
throw "error";
}
pp
[[ noreturn ]] void q(int i ) {
if ( i > 0 ) {
throw "positive";
}
}
[[carries_dependency]]
該屬性是個優化屬性,它能夠優化使用了memory_order_consume
的代碼。它的作用就是轉移使用memory_order_consume
而形成的依賴,將原先通過插入柵指令維持依賴樹的方法變爲有我們自己來編碼維持依賴,從而達到優化的目的。如果一個通過memory_order_consume
的值被傳遞給一個函數的參數,那麼如果沒有使用[[carries_dependency]]
屬性,那麼編譯器可能會使用一個內存柵指令去保證合適的memory order
語義的支持,如果一個參數被[[carries_dependency]]
註釋,那麼編譯器就假定函數體會保存依賴,從而不必生成內存柵,達到優化的目的。同樣如果一個函數的返回值以memory_order_consume
的方式讀取,如果沒有被[[carries_dependency]]
修飾,那麼編譯器需要插入一個柵指令來保證合適memory order
語義的支持。如果聲明[[carries_dependency]]
,那麼柵指令就不必要的了,依賴樹轉由調用者維持。
由上面也知道,[[carries_dependency]]
屬性只能應用到函數或lamda表達式的參數處,或者返回值處。
[[deprecated]]/[[deprecated("reason)]
[[deprecated]]
void foo() {};
[[deprecated("推薦使用foo2")]]
void foo1(){};
void foo2(){};
int main(){
foo();
foo1
();
}
└[/mnt/D/Developer/WorkPlace/Personal-Workplace-Temp/C++/Amateur/src]> g++ -std=c++14 AttributeDeprecated.cpp -o ../bin/AttributeDeprecated
AttributeDeprecated.cpp: In function ‘int main()’:
AttributeDeprecated.cpp:14:3: warning: ‘void foo()’ is deprecated (declared at AttributeDeprecated.cpp:2) [-Wdeprecated-declarations]
foo(); // we're using a deprecated function
^
AttributeDeprecated.cpp:14:7: warning: ‘void foo()’ is deprecated (declared at AttributeDeprecated.cpp:2) [-Wdeprecated-declarations]
foo(); // we're using a deprecated function
^
AttributeDeprecated.cpp:15:3: warning: ‘void foo1()’ is deprecated (declared at AttributeDeprecated.cpp:6): 推薦使用foo2 [-Wdeprecated-declarations]
foo1();
^
AttributeDeprecated.cpp:15:8: warning: ‘void foo1()’ is deprecated (declared at AttributeDeprecated.cpp:6): 推薦使用foo2 [-Wdeprecated-declarations]
foo1();
^
注意
[[deprecated]]
修飾類和枚舉類型是,聲明在class
或者enum
前後有區別,如果聲明在class
或者enum
之前,說明這個類或者枚舉類型是廢棄的; 反之,就說明這個變量是廢棄的。而對於其他類型來說[[deprecated]]
只能放在類型定義符號之前。
[[nodiscard]]
[[nodiscard]]
出現在函數,枚舉,結構體或者類聲明中。如果一個函數聲明爲nodiscard
或者一個返回值爲聲明nodiscard
了枚舉類型或者類的函數被一個discard-value
表達式調用,那麼編譯器將會報錯。
[[nodiscard]] int something(){
return 1;
}
int main() {
int a = something();
}
AttributeNodiscard.cpp: In function ‘int main()’:
AttributeNodiscard.cpp:11:8: warning: unused variable ‘a’ [-Wunused-variable`]
int a = something();
^
[[maybe_unused]]
[[maybe_unused]]
主要用來修飾類的聲明,類型定義,變量,非靜態數據成員,函數,枚舉類型,枚舉器。 如果編譯器對一些未使用的實體生成警告信息,這樣的信息可以使用[[maybe_unused]]
屬性抑制。
[[nodiscard]] int something() {
return 1;
}
int main() {
[[maybe_unused]] int a = something();
}
你會發現上面生成的警告信息已經被抑制了。
[[fallthrough]]
[[fallthrough]]
主要放在switch-case
語句中,位置放在case
語句之上。[[fallthrough]]
語句可以避免case
有表達式但是沒有break
而導致的fallthrough
警告。
void something() {
}
void fallthrough() {
int i = 0;
switch(i) {
case 1:
case 2:
something();
case 3:
something();
case 4:
something();
default:
break;
}
}
AttributeFallthrough.cpp: In function ‘void fallthrough()’:
AttributeFallthrough.cpp:13:14: warning: this statement may fall through [-Wimplicit-fallthrough=]
something();
~~~~~~~~~^~
AttributeFallthrough.cpp:14:3: note: here
case 2 :
^~~~
AttributeFallthrough.cpp:15:14: warning: this statement may fall through [-Wimplicit-fallthrough=]
something();
~~~~~~~~~^~
AttributeFallthrough.cpp:17:3: note: here
case 3:
^~~~
AttributeFallthrough.cpp:18:14: warning: this statement may fall through [-Wimplicit-fallthrough=]
something();
~~~~~~~~~^~
AttributeFallthrough.cpp:20:3: note: here
case 4:
^~~~
void something() {
}
void fallthrough() {
int i = 0;
switch(i) {
case 1:
case 2:
something();
[[fallthrough]];
case 3:
something();
[[fallthrough]];
case 4:
something();
[[fallthrough]];
default:
break;
}
}
Note:你會發現上面的錯誤已經消失了。
生成fallthrought警告的條件
case
語句中有表達式但是沒有break
語句,時編譯器會產生fallthrough
警告。如果case
語句中沒有任何表達式,編譯器不會產生fallthrough
警告。如果case
語句中有表達式,但是以後的case
語句中沒有語句,並最後的有break
結尾,那麼編譯器不會產生fallthrough
警告
void something() {
}
void fallthrough() {
int i = 0;
switch(i) {
case 1:
//1
case 2:
something(); //2
case 3:
something(); //3
case 4:
case 5:
default:
break;
}
}
1
處和3
處不會產生fallthrough
警告, 但是2
處會產生fallthrough
警告。
Note 要顯示fallthrough
警告錯誤,g++編譯器爲7.0並且g++ 編譯器需要添加一下編譯參數-std=c++1z
, -Wextra
,
注意 這個屬性一定要出現在函數的第一次聲明中或者在任何翻譯單元的一個屬性中。否則程序將會報錯。
#include <iostream>
#include <atomic>
std::atomic<int *> p;
void print( int * val )
{
std::cout<< *p << std::endl;
}
void print2(int * [[carries_dependency]] val)
{
std::cout << *p << std::endl;
}
int main () {
int * local = p.load(std::memory_order_consume);
if (local)
std::cout << *local<<std::endl; // 1
if (local)
print(local); //2
if(local)
print2(local); //3
}
1處的代碼,依賴很清楚,所以編譯器知道local
變量是廢棄的,所以它必須確保依賴鏈被保存,避免內存柵的生成。
2處的代碼,print
的定義是不透明的,所以編譯器必須生成一個內存柵爲了,讀取*p
的值的print
函數能夠返回正確的值。
3處的代碼,編譯器可以假定,就算print2
的定義也是不透明的,那麼從參數道廢棄的值的依賴被保存在指令流中,所以並不一定需要生成內存柵。很顯然,print2
的定義確實保存了這個依賴,所以[[carries_dependency]]
對print2
的代碼生成有一定的影響。