跳轉到內容

更多 C++ 習語/SFINAE

來自 Wikibooks,開放世界中的開放書籍

從一組過載函式中刪除那些無法生成有效模板例項化的函式。

也稱為

[編輯 | 編輯原始碼]

Substitution Failure Is Not An Error(替換失敗並非錯誤)

動機和解決方案

[編輯 | 編輯原始碼]

嚴格來說,SFINAE 是一個語言特性,而不是一個習語。然而,這個語言特性在使用 enable-if 時被以非常習慣性的方式利用。

在模板引數推導過程中,C++ 編譯器會嘗試例項化多個候選過載函式的簽名,以確保只有一個過載函式可以完美匹配給定的函式呼叫。如果在函式模板例項化期間形成了無效的引數或返回值型別,則該例項化將從過載解析集中刪除,而不是導致編譯錯誤。只要只有一個函式可以呼叫,編譯器就不會發出任何錯誤。

例如,考慮一個簡單的函式 multiply 及其模板化對應物。

long multiply(int i, int j) { return i * j; }

template <class T>
typename T::multiplication_result multiply(T t1, T t2)
{
  return t1 * t2;
}
int main(void)
{
  multiply(4,5);
}

main 中呼叫函式 multiply 會導致編譯器嘗試例項化模板化函式的簽名,即使第一個 multiply 函式是更好的匹配。在例項化期間,會產生一個無效型別:int::multiplication_result。然而,由於 SFINAE,這種無效例項化會自動被忽略。最終,只有一個 multiply 函式可以被呼叫,即第一個函式。因此編譯成功。

SFINAE 通常被用來在編譯時確定型別的屬性。例如,考慮以下 is_pointer 元函式,它在編譯時確定給定型別是否為某種型別的指標。

template <class T>
struct is_pointer
{
  template <class U>
  static char is_ptr(U *);

  template <class X, class Y>
  static char is_ptr(Y X::*);

  template <class U>
  static char is_ptr(U (*)());

  static double is_ptr(...);

  static T t;
  enum { value = sizeof(is_ptr(t)) == sizeof(char) };
};

struct Foo {
  int bar;
};

int main(void)
{
  typedef int * IntPtr;
  typedef int Foo::* FooMemberPtr;
  typedef int (*FuncPtr)();

  printf("%d\n",is_pointer<IntPtr>::value);        // prints 1
  printf("%d\n",is_pointer<FooMemberPtr>::value);  // prints 1
  printf("%d\n",is_pointer<FuncPtr>::value);       // prints 1
}

上面的 is_pointer 元函式如果沒有 SFINAE 就不會起作用。它定義了 4 個過載的 is_ptr 函式,其中 3 個是模板,每個模板都接受一個引數:指向變數的指標、指向成員變數的指標或簡單的函式指標。所有三個函式都返回一個 char,這是故意的。最後一個 is_ptr 函式是一個通配函式,使用省略號作為引數。但是,這個函式返回一個 double,它的大小始終大於字元。

is_pointer 傳遞一個實際上是指標的型別(例如,IntPtr)時,由於兩個 sizeof 表示式的比較,value 會被初始化為 true。第一個 sizeof 表示式呼叫 is_ptr。如果它是一個指標,只有一個過載的模板函式匹配,而不是其他函式。但是,由於 SFINAE,不會引發錯誤,因為至少找到了一個合適的函式。如果沒有任何函式適合,則會使用帶有省略號的函式。但是,該函式返回一個 double,它大於字元,因此 sizeof 比較失敗,value 被初始化為 false。

請注意,所有 is_ptr 函式都沒有定義。只有宣告足以觸發編譯器中的 SFINAE 規則。但是,這些函式本身必須是模板。也就是說,具有常規函式的類模板將不會參與 SFINAE。參與 SFINAE 的函式必須是模板。

已知用法

[編輯 | 編輯原始碼]
[編輯 | 編輯原始碼]

參考文獻

[編輯 | 編輯原始碼]
華夏公益教科書