名稱空間
變體
操作

泛型選擇 (自 C11 起)

來自 cppreference.com
< c‎ | 語言

提供了一種在編譯時根據控制表示式的型別選擇多個表示式之一的方式

目錄

[編輯] 語法

_Generic ( 控制表示式 , 關聯列表 ) (C11 起)

其中 關聯列表 是以逗號分隔的關聯列表,每個關聯的語法如下:

型別名稱 : 表示式
default : 表示式

其中

型別名稱 - 任何完整的物件型別,且不是可變修改的(即不是 VLA 或指向 VLA 的指標)。
控制表示式 - 任何表示式(除了逗號運算子),如果未使用 default 關聯,則其型別必須與型別名稱之一相容。
表示式 - 任何型別和值類別的表示式(除了逗號運算子

關聯列表中不能有兩個型別名稱指定相容型別。只能有一個使用關鍵字 default 的關聯。如果未使用 default 且沒有任何型別名稱與控制表示式的型別相容,則程式將無法編譯。

[編輯] 解釋

首先,控制表示式的型別會進行左值轉換。轉換僅在型別域中執行:它丟棄頂層 cvr-限定符和原子性,並將陣列到指標/函式到指標的轉換應用於控制表示式的型別,而不引發任何副作用或計算任何值。

轉換後的型別將與關聯列表中的型別名稱進行比較。

如果型別與某個關聯的型別名稱相容,則泛型選擇的型別、值和值類別就是該型別名稱後出現的表示式的型別、值和值類別。

如果沒有任何型別名稱控制表示式的型別相容,並且提供了 default 關聯,則泛型選擇的型別、值和值類別就是 default : 標籤後表示式的型別、值和值類別。

[編輯] 注意

控制表示式和未被選擇的表示式永遠不會被求值。

由於左值轉換,"abc" 匹配 char* 而不是 char[4],並且 (int const){0} 匹配 int,而不是 const int

所有值類別,包括函式指示符和 void 表示式,都允許作為泛型選擇中的表示式,如果被選中,泛型選擇本身具有相同的值類別。

C99 中引入的來自<tgmath.h>型別泛型數學宏是以編譯器特定方式實現的。C11 中引入的泛型選擇使程式設計師能夠編寫類似的型別依賴程式碼。

泛型選擇類似於 C++ 中的過載(其中根據引數型別在編譯時選擇多個函式之一),不同之處在於它在任意表達式之間進行選擇。

[編輯] 關鍵字

_Generic, default

[編輯] 示例

#include <math.h>
#include <stdio.h>
 
// Possible implementation of the tgmath.h macro cbrt
#define cbrt(X) _Generic((X),     \
              long double: cbrtl, \
                  default: cbrt,  \
                    float: cbrtf  \
              )(X)
 
int main(void)
{
    double x = 8.0;
    const float y = 3.375;
    printf("cbrt(8.0) = %f\n", cbrt(x));    // selects the default cbrt
    printf("cbrtf(3.375) = %f\n", cbrt(y)); // converts const float to float,
                                            // then selects cbrtf
}

輸出

cbrt(8.0) = 2.000000
cbrtf(3.375) = 1.500000

[編輯] 缺陷報告

以下行為改變的缺陷報告被追溯地應用於以前釋出的 C 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
DR 481 C11 未明確控制表示式是否進行左值轉換 它進行了

[編輯] 參考

  • C23 標準 (ISO/IEC 9899:2024)
  • 6.5.1.1 泛型選擇 (p: 待定)
  • C17 標準 (ISO/IEC 9899:2018)
  • 6.5.1.1 泛型選擇 (p: 56-57)
  • C11 標準 (ISO/IEC 9899:2011)
  • 6.5.1.1 泛型選擇 (p: 78-79)

[編輯] 另請參閱

C++ 文件,關於 模板