C++ 標準會定義一組常見的屬性。 它也允許編譯程式廠商在廠商特定的命名空間內定義自己的屬性。 不過,編譯程式只需要辨識在標準中定義的屬性。
在某些情況下,標準屬性會與編譯程式特定的參數重疊。 在 Microsoft C++中,您可以使用屬性,而不是使用。 屬性是由任何一個符合的編譯程式所辨識。 對於和等所有其他參數,到目前為止沒有任何屬性相等,因此您必須繼續使用語法。 屬性不會影響類型系統,且不會變更程式的意義。 編譯程式會忽略它們無法辨識的屬性值。
Visual Studio 2017 版本 15.3 及以上版本(可隨 /std:c++17 及以上版本提供):在屬性清單的範圍內,你可以用單一的 using 介紹器指定所有名稱的命名空間:
void g() {
[[using rpr: kernel, target(cpu,gpu)]] // equivalent to [[ rpr::kernel, rpr::target(cpu,gpu) ]]
do task();
}
C++ 標準屬性
在 C++11 中,屬性會提供一種標準化的方式來標註 C++ 建構 (包括但不限於類別、函式、變數和區塊),並提供其他資訊。 屬性不一定是廠商特定的。 編譯程式可以使用這項資訊來產生參考訊息,或在編譯屬性程式碼時套用特殊邏輯。 編譯程式會忽略它無法辨識的任何屬性,這表示您無法使用此語法來定義自己的自訂屬性。 屬性會以雙方括弧括住:
[[deprecated]]
void Foo(int);
屬性代表廠商特定擴充的標準化替代方案,例如指示詞、(Visual C++),或 (GNU)。 不過,針對大部分用途,您仍然需要使用廠商特定的建構。 標準目前指定符合的編譯程式應該可辨識的下列屬性。
[[carries_dependency]]
屬性會指定會傳播線程同步處理的資料相依性順序的函式。 屬性可以套用至一或多個參數,以指定傳入的自變數將相依性帶入函式主體。 屬性可以套用至函式本身,以指定傳回值將相依性帶出函式。 編譯程式可以使用這項資訊來產生更有效率的程式碼。
[[deprecated]]
Visual Studio 2015 及以後:[[deprecated]] 屬性指定該函式不適合使用。 或者,它可能不存在於程式庫介面的未來版本中。 屬性可以套用至類別的宣告、typedef-name、變數、非靜態資料成員、函式、命名空間、列舉值、列舉值,或範本特製化。 當用戶端程式碼嘗試呼叫函式時,編譯程式可以使用這個屬性來產生參考訊息。 當 Microsoft C++ 編譯程式偵測到項目的使用時,它會引發編譯程式警告 C4996。
[[fallthrough]]
Visual Studio 2017 及以後:(可與 /std:c++17 及以後版本提供。)[[fallthrough]] 屬性可在 switch 語句的語境中作為提示,提醒編譯器(或任何閱讀程式碼的人)該 fallthrough 行為是有意設計的。 Microsoft C++ 編譯器目前不會針對繼續執行發出警告,因此這個屬性不會影響編譯器行為。
[[likely]]
Visual Studio 2019 版本 16.6 及以上:(可隨 /std:c++20 及後版本提供。)[[likely]] 屬性指定一個提示編譯器,表示該標籤或語句的程式碼路徑比其他選項更有可能執行。 在 Microsoft 編譯器中,屬性 將區塊標記為「熱程式碼」,並增加內部優化分數。 針對速度進行最佳化時,分數會增加更多,在最佳化尺寸時不會增加那麼多。 淨分數會影響內嵌、循環展開和向量化最佳化的可能性。 和的效果與設定檔引導最佳化類似,但僅限於目前翻譯單位的範圍。 尚未實施此屬性的區塊重新排序最佳化。
[[maybe_unused]]
Visual Studio 2017 版本 15.3 及以後版本:(可隨 /std:c++17 及以上版本提供。)[[maybe_unused]] 屬性指定變數、函式、類別、類型def、非靜態資料成員、枚舉或範本專用化可以被刻意不使用。 未使用標示為的實體時,編譯程式不會發出警告。 宣告時沒有屬性的實體,稍後可以使用屬性重新宣告,反之亦然。 在分析完第一個標示為的宣告後,會將實體以及目前翻譯單元的其餘部分視為。
[[nodiscard]]
Visual Studio 2017 版本 15.3 及以後版本:(可隨 /std:c++17 及以上版本提供。)規定函式的回傳值不應被捨棄。 引發警告 C4834,如下列範例所示:
[[nodiscard]]
int foo(int i) { return i * i; }
int main()
{
foo(42); //warning C4834: discarding return value of function with 'nodiscard' attribute
return 0;
}
[[noreturn]]
屬性會指明函式永遠不會回傳:換句話說,它一律會丟掉例外狀況或退出。 編譯程式可以調整實體的編譯規則。
[[unlikely]]
Visual Studio 2019 版本 16.6 及以上:(可隨 /std:c++20 及後版本提供。)[[unlikely]] 屬性指定一個提示編譯器,表示該標籤或語句的程式碼路徑比其他選項執行的可能性較低。 在 Microsoft 編譯程式中,屬性會將區塊標示為「冷門程式碼」,這樣會減少內部最佳化分數。 針對尺寸大小進行最佳化時,分數會減少更多,在最佳化速度時不會減少那麼多。 淨分數會影響內嵌、循環展開和向量化最佳化的可能性。 尚未實施此屬性的區塊重新排序最佳化。
Microsoft 特定屬性
[[gsl::suppress(<tag> [, justification: <narrow-string-literal>])]]
此Microsoft專屬屬性於 2022 Visual Studio版本 17.14 引入,抑制執行 指引支援函式庫(GSL)規則的檢查員警告。 屬性可以套用在語句、區塊或宣告上。 它只支援 C++。 對於 C 程式碼,請使用 代替。
是一個字串,用於指定要隱藏的規則的名稱。 該 optional 字段允許您解釋禁用或禁止顯示警告的原因。 當指定該選項時,此值會出現在靜態分析結果交換格式(SARIF)輸出中。 它的值是UTF-8編碼的窄字串文字。 要產生 SARIF 檔案,請使用 編譯器選項。
範例:
int main()
{
int arr[10]; // GSL warning C26494 will be fired
int* p = arr; // GSL warning C26485 will be fired
[[gsl::suppress("bounds.1", justification: "This attribute suppresses Bounds rule #1")]]
{
int* q = p + 1; // GSL warning C26481 suppressed
p = q--; // GSL warning C26481 suppressed
}
}
這個例子提出了以下警示:
- C26494 (類型規則 5:一律初始化物件。)
- C26485 (界限規則 3:指標退化沒有陣列。)
- C26481 (界限規則 1:不要使用指標算術。請改用範圍。)
前兩個警告是在你編譯並啟用 CppCoreCheck 程式碼分析工具時出現。 但因為屬性,所以不會引發第三個警告。 您可以透過撰寫來隱藏整個界限設定檔,而不用包含特定的規則編號。 C++ 核心指導方針旨在協助您撰寫更好且更安全的程式碼。 隱藏屬性可讓您在不想要時輕鬆關閉警告。
在 和 之間 選擇
兩者皆提供對警告抑制的細緻控制:
-
[[gsl::suppress]]僅抑制由 C++ Code Analysis 發出的警告Microsoft。 搭配 C++ 核心指引檢查一起使用,這些檢查可以套用到作用域或特定宣告。 - 可用於任何編譯器警告。 當你需要在不大幅改變程式碼結構的情況下,抑制特定程式碼區塊的警告時,它非常有用。
盡可能使用 [[gsl::suppress]] 來抑制Microsoft C++ Code Analysis警告。
[[msvc::flatten]]
Microsoft 專屬屬性 與 類似,且可在相同地點及方式使用。 差別在於會在範圍中以遞迴方式套用所有呼叫,直到沒有呼叫為止。 這可能會影響函式產生的程式碼大小成長或編譯程式的總處理能力,您必須手動管理。
[[msvc::forceinline]]
放置在函式宣告之前時,Microsoft 特定屬性的意義與相同。
[[msvc::forceinline_calls]]
Microsoft 特定屬性可以放在陳述式或區塊上或之前。 它會導致內嵌啟發法嘗試 在該陳述式或區塊中的所有呼叫:
void f() {
[[msvc::forceinline_calls]]
{
foo();
bar();
}
...
[[msvc::forceinline_calls]]
bar();
foo();
}
對的第一個呼叫,和對的兩個呼叫,都會被視為它們被宣告。 對的第二個呼叫不會被視為。
[[msvc::intrinsic]]
屬性在套用時的函式上有三個條件約束:
- 函式無法遞迴:其正文必須只有從參數類型到退回類型,且具有的退回陳述式。
- 函式只能接受單一參數。
- 必須有編譯程式選項。 (和之後的選項預設為隱含。)
Microsoft 特定屬性會要求編譯程式內嵌元功能,其作用是從參數類型到退回類型的具名轉換。 當函式定義上有屬性時,編譯程式會將該函式的所有呼叫取代為簡單轉換。
[[msvc::intrinsic]] 屬性可在 2022 Visual Studio 版本 17.5 預覽版及更新版本中使用。 此屬性僅適用於其後面的特定函式。
範例
在此樣本程式碼中,套用至 函式的屬性會讓編譯程式使用其正文中的內嵌靜態轉換來取代對函式的呼叫:
template <typename T>
[[msvc::intrinsic]] T&& my_move(T&& t) { return static_cast<T&&>(t); }
void f() {
int i = 0;
i = my_move(i);
}
[[msvc::musttail]]
該 屬性於 MSVC 建構工具 14.50 版本中引入,是一個實驗性僅限 x64 的 Microsoft 專屬屬性,能強制執行尾部呼叫優化。 當應用於限定回傳語句時,會指示編譯器以尾部呼叫方式發出呼叫。 如果編譯器無法發出尾部呼叫,就會產生編譯錯誤。 屬性 強制執行尾部呼叫,而非內嵌函式。
需求:
- 來電者與被叫者必須擁有相符的回傳類型。
- 呼叫慣例必須相容。
- 尾部呼叫必須是呼叫函式的最後一個動作。
- 被callee不能使用比呼叫函式更多的堆疊空間。
- 若傳遞超過四個整數參數,呼叫函數必須分配足夠的堆疊空間給其他參數。
- 編譯時會調整 到 優化等級。
範例
尾部調用是一種編譯器優化,當函式呼叫是返回前最後執行的動作時所能實現。 與其建立新的堆疊框架來呼叫該函式,不如重複使用現有函式的堆疊框架。 這能減少堆疊使用並提升效能——尤其是在遞迴情境下。
在以下程式碼中, 套用到 的 屬性會直接將控制權轉移到 。 當 到達該 陳述時,其結果會直接提供給 的 呼叫者,即 。 這取代了 內列進入 或呼叫 至 ,然後返回 , 然後再回到 。 尾端呼叫優化消除了結束 後 重新取得控制權的需求。 這是一種效能優化,能減少堆疊使用,尤其適用於遞迴情境。
// compile with /O2
#include <iostream>
int increment(int x)
{
return x + 1;
}
int incrementIfPositive(int x)
{
if (x > 0)
{
[[msvc::musttail]]
return increment(x);
}
return -1;
}
int main()
{
int result = incrementIfPositive(42);
if (result < 0)
{
return -1;
}
std::cout << result; // outputs 43
return 0;
}
[[msvc::noinline]]
放置在函式宣告之前時,Microsoft 特定屬性的意義與相同。
[[msvc::noinline_calls]]
Microsoft 特定屬性的用法與相同。 它可以放在任何陳述式或區塊之前。 它的作用是關閉套用範圍內的內嵌,而不是強制內嵌該區塊中的所有呼叫
[[msvc::no_tls_guard]]
Microsoft 特定 屬性會停用第一次存取 DLL 中 thread-local 變數的初始化檢查。 這些檢查預設在使用 Visual Studio 2019 版本 16.5 及更新版本所建構的程式碼中啟用。 此屬性僅適用於其後面的特定變數。 若要全域停用檢查,請使用編譯程式選項。