現代 C (C11/C17/C23)

_Generic 型別泛型選擇(type-generic selection)

/ JEN-er-ik /

想像你要一個簡短的名字,例如 abs(x),無論 x 是 int、long、float 還是 double 都能做對的事,並為每種型別挑選不同的真正函式。純 C 沒有多載(overloading),所以過去你得為每種型別寫不同的名字。C11 加入的 _Generic 是一種對運算式「型別」做的編譯期分派,讓一個巨集能挑出相符的分支。

_Generic 用文字描述像這樣:_Generic(控制運算式, 型別1: 結果1, 型別2: 結果2, default: 後備)。編譯器不會執行任何東西;它檢視控制運算式的靜態型別,與列出的型別比對,整個 _Generic 在任何程式碼執行前就被選中的結果運算式「取代」。控制運算式本身從不被求值,只檢查它的型別(會先套用左值轉右值、陣列與函式退化)。它幾乎總是包在巨集裡,讓單一呼叫點依型別分派。例如巨集 cbrt(x) 可能為 float 選 cbrtf、為 double 選 cbrt、為 long double 選 cbrtl。

它重要在於:這是 C 中唯一標準、可移植、能寫出依型別導向程式碼的方式,不必動用 C++ 樣板或雜亂的前置處理器技巧。標準標頭檔 <tgmath.h> 就建立在它之上。一個常見的意外:未被選中的分支仍必須是剖析器能接受的合法運算式(不會對錯誤型別做型別檢查,但必須能剖析),而且比對時沒有隱式轉換,所以即使在 int 與 long 同寬度的平台上,它們仍是不同的分支。

#define type_name(x) _Generic((x), int: "int", double: "double", char *: "string", default: "other") // type_name(3) -> "int" // type_name(2.0) -> "double"

一個小巧的型別轉字串巨集。比對的是引數的靜態型別,完全在編譯期決定。

比對是精確的:沒有隱式轉換,因此字串常值的型別是 char[N](常透過 char * 比對),而 'a' 的型別是 int 而非 char——請列出你真正預期的型別。

又称
generic selection_Generic型別分派的編譯期 switch