C++ 中的屬性

C++ 標準會定義一組常見的屬性。 它也允許編譯程式廠商在廠商特定的命名空間內定義自己的屬性。 不過,編譯程式只需要辨識標準中定義的屬性。

在某些情況下,標準屬性會與編譯程式特定的 __declspec 參數重疊。 在 Microsoft C++ 中,您可以使用 [[deprecated]] 屬性,而不是使用 __declspec(deprecated)。 屬性 [[deprecated]] 是由任何一個符合的編譯程式所辨識。 針對 和 dllexportdllimport所有其他__declspec參數,到目前為止沒有任何屬性相等,因此您必須繼續使用__declspec語法。 屬性不會影響類型系統,而且不會變更程序的意義。 編譯程式會忽略它們無法辨識的屬性值。

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);

屬性代表廠商特定延伸模組的標準化替代方案,例如 #pragma 指示詞、 __declspec() (Visual C++) 或 __attribute__ (GNU)。 不過,您仍然需要針對大部分用途使用廠商特定的建構。 標準目前指定符合編譯程式應該辨識的下列屬性。

[[carries_dependency]]

屬性 [[carries_dependency]] 指定函式會傳播線程同步處理的數據相依性順序。 屬性可以套用至一或多個參數,以指定傳入的自變數會將相依性帶入函式主體。 屬性可以套用至函式本身,以指定傳回值會將相依性帶出函式。 編譯程式可以使用這項資訊來產生更有效率的程序代碼。

[[deprecated]]

Visual Studio 2015 和更新版本: 屬性 [[deprecated]] 會指定函式不適合使用。 或者,它可能不存在於連結庫介面的未來版本中。 屬性 [[deprecated]] 可以套用至類別的宣告、typedef-name、變數、非靜態數據成員、函式、命名空間、列舉值、列舉值或範本特製化。 當用戶端程式代碼嘗試呼叫 函式時,編譯程式可以使用這個屬性來產生參考訊息。 當 Microsoft C++ 編譯程式偵測到專案使用時 [[deprecated]] ,它會引發編譯程式警告 C4996

[[fallthrough]]

Visual Studio 2017 和更新版本: (適用於 /std:c++17 和更新版本。屬性 [[fallthrough]] 可用於語句的內容 switch 中,做為編譯程式(或讀取程序代碼的任何人)所要執行之後置行為的提示。 Microsoft C++ 編譯程式目前不會在後置行為上發出警告,因此此屬性不會影響編譯程序行為。

[[likely]]

Visual Studio 2019 16.6 版和更新版本: (適用於 /std:c++20 和更新版本。屬性 [[likely]] 會指定編譯程式的提示,指出屬性標籤或語句的程式代碼路徑比替代專案更可能執行。 在 Microsoft 編譯程式中,屬性會將 [[likely]] 區塊標示為「經常性程式代碼」,以遞增內部優化分數。 針對速度優化時,分數會遞增更多,而且在優化大小時不會增加。 淨分數會影響內嵌、迴圈取消卷動和向量化優化的可能性。 和 [[unlikely]] 的效果[[likely]]配置檔引導優化類似,但僅限於目前翻譯單位的範圍。 尚未針對此屬性實作區塊重新排序優化。

[[maybe_unused]]

Visual Studio 2017 15.3 版和更新版本: (適用於 /std:c++17 和更新版本。屬性 [[maybe_unused]] 指定變數、函式、類別、typedef、非靜態數據成員、列舉或範本特製化可能刻意未使用。 未使用標示 [[maybe_unused]] 的實體時,編譯程式不會發出警告。 若未宣告屬性的實體,稍後可以使用 屬性重新宣告,反之亦然。 在標示為已分析的第一個宣告之後,以及目前翻譯單元的其餘部分,會將實體視為標示[[maybe_unused]]

[[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]]

屬性 [[noreturn]] 會指定函式永遠不會傳回;換句話說,它一律會擲回例外狀況或結束。 編譯程式可以調整其實體的 [[noreturn]] 編譯規則。

[[unlikely]]

Visual Studio 2019 16.6 版和更新版本: (適用於 /std:c++20 和更新版本。屬性 [[unlikely]] 會指定編譯程式的提示,指出屬性卷標或語句的程式代碼路徑不太可能執行,而不是替代專案。 在 Microsoft 編譯程式中,屬性會將 [[unlikely]] 區塊標示為「冷程式代碼」,這會遞減內部優化分數。 針對大小進行優化時,分數會遞減更多,而且在優化速度時不會減少太多。 淨分數會影響內嵌、迴圈取消卷動和向量化優化的可能性。 尚未針對此屬性實作區塊重新排序優化。

Microsoft 特定屬性

[[gsl::suppress(rules)]]

Microsoft 特定的 [[gsl::suppress(rules)]] 屬性可用來隱藏程式代碼中強制執行 指導方針支持連結庫 (GSL) 規則的檢查程式警告。 例如,請考慮此代碼段:

int main()
{
    int arr[10]; // GSL warning C26494 will be fired
    int* p = arr; // GSL warning C26485 will be fired
    [[gsl::suppress(bounds.1)]] // 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 程式代碼分析工具編譯此程式代碼時,會引發前兩個警告。 但第三個警告不會因為 屬性而引發。 您可以藉由撰寫 [[gsl::suppress(bounds)]] 來隱藏整個界限配置檔,而不包含特定的規則編號。 C++ 核心指導方針旨在協助您撰寫更好且更安全的程序代碼。 隱藏屬性可讓您在不想要時輕鬆關閉警告。

[[msvc::flatten]]

Microsoft 特定屬性 [[msvc::flatten]] 與 非常類似 [[msvc::forceinline_calls]],而且可以用相同方式在相同位置使用。 差別在於 [[msvc::flatten]] ,它會 [[msvc::forceinline_calls]] 在範圍中以遞歸方式套用所有呼叫,直到沒有呼叫為止。 這可能會對函式產生的程式代碼大小成長或編譯程式的輸送量產生後果,您必須手動管理。

[[msvc::forceinline]]

在函式宣告之前放置時,Microsoft 特定屬性 [[msvc::forceinline]] 的意義與 __forceinline相同。

[[msvc::forceinline_calls]]

Microsoft 特定屬性 [[msvc::forceinline_calls]] 可以放在 語句或區塊之前或之前。 它會導致內嵌啟發學習法嘗試 [[msvc::forceinline]] 在該語句或區塊中的所有呼叫:

void f() {
    [[msvc::forceinline_calls]]
    {
        foo();
        bar();
    }
    ...
    [[msvc::forceinline_calls]]
    bar();
    
    foo();
}

對的第一個呼叫 和對 的兩個呼叫foobar都會被視為宣告 __forceinline。 的第二個呼叫 foo 不會被視為 __forceinline

[[msvc::intrinsic]]

屬性 [[msvc::intrinsic]] 在套用至的函式上有三個條件約束:

  • 函式無法遞歸;其主體必須只有從參數型別到傳回型別的 return 語句 static_cast
  • 函式只能接受單一參數。
  • 需要編譯 /permissive- 程序選項。 (和 /std:c++20 更新版本選項預設為隱含 /permissive-

Microsoft 特定 [[msvc::intrinsic]] 屬性會告知編譯程式內嵌中繼函式,做為從參數類型轉換成傳回型別的具名轉換。 當函式定義上有 屬性時,編譯程式會將該函式的所有呼叫取代為簡單的轉換。 屬性 [[msvc::intrinsic]] 可在Visual Studio 2022 17.5版 Preview 2 和更新版本中取得。 此屬性僅適用於其後面的特定函式。

範例

在此範例程式代碼中 [[msvc::intrinsic]] ,套用至 my_move 函式的 屬性會讓編譯程式使用其主體中的內嵌靜態轉換來取代對函式的呼叫:

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::noinline]]

在函式宣告之前放置時,Microsoft 特定屬性 [[msvc::noinline]] 的意義與 declspec(noinline)相同。

[[msvc::noinline_calls]]

Microsoft 特定屬性 [[msvc::noinline_calls]] 的用法與 [[msvc::forceinline_calls]]相同。 它可以放在任何語句或區塊之前。 與其強制內嵌該區塊中的所有呼叫,而是對套用的範圍關閉內嵌的效果。

[[msvc::no_tls_guard]]

Microsoft 特定 [[msvc::no_tls_guard]] 屬性會停用第一次存取 DLL 中線程局部變數的初始化檢查。 預設會在使用 Visual Studio 2019 16.5 版和更新版本所建置的程式代碼中啟用檢查。 此屬性只適用於其後面的特定變數。 若要全域停用檢查,請使用編譯 /Zc:tlsGuards- 程序選項。