建議閱讀原文
例如我們有一個函數模闆将兩個對象 v1, v2 相加得到 v
template
如果我們想要對不同類型的 T,T1,T2 執行不同的代碼, 我強烈推薦下面要介紹的 SFINAE 技巧來實作。
我們先來定義一個宏函數 MY_IF()(至于為什麼要這麼定義先不介紹),其中使用了 type_traits 頭檔案中的 enable_if。 這個宏用于輸入一個 constexpr 的 bool 表達式, 由于表達式中有可能出現一個或多個逗号,是以形式上我們用了任意變量的宏函數。
#define MY_IF0(...) typename std::enable_if<(bool)(__VA_ARGS__), Int>::type
這裡先定義了 MY_IF0(),看起來多此一舉,但實際上在一些情況下我們也需要單獨使用 MY_IF0()。
另外, 我們假設存在一些函數模闆用于判斷 T, T1, T2 的類型(具體怎麼定義先不介紹), 例如如果 T 是一個矩陣, is_matrix<T>() 就傳回 true, 而 is_scalar<T>() 和 is_vector<T>() 都傳回 false。
template
現在我們用 MY_IF 來區分不同版本的 Plus 函數。 标量相加的函數如下
template
注意在我們通過 MY_IF 聲明了這個模闆什麼時候有定義(隻有 T,T1,T2 為标量時有定義, 例如 int, double, complex 等)。
同樣, 我們可以再寫一個版本的 Plus 定義矢量相加
template
我們還可以再定義矩陣相加,矩陣與标量相加,矢量與标量相加等等。
如果我們的函數需要先 declare 再 define(例如 class 的成員函數在 class 定義中 declare,然後在别的地方 define),那就需要在 declaration 中使用 MY_IF(),而在 definition 中使用 MY_IF0(),其它内容都一樣。
使用 SFINAE 的好處是, 我們可以限制函數模闆 instantiate 的條件, 使得一些不合法的使用變得沒有定義(比如說我想要用 Plus 把矩陣和矢量相加,又比如複數矩陣相加得到實數矩陣)。 另一個好處是無論我們定義多少個版本的 Plus, 隻要 MY_IF 中的條件總在唯一一個版本中為 true, 編譯器就不會抱怨無法判斷使用哪個版本的 Plus 函數。
至于 is_scalar(), is_vector(), is_matrix() 等函數如何實作, 以後再介紹, 有興趣的同學可以先了解一下标準庫中的 type_trait 頭檔案。
在我寫的 SLISC 項目中, 以上的技巧被大量使用。 有興趣的同學可以關注
Scientific Library In Simple C++github.com