TMPの条件分岐を記載する場所

  • 返り値型がvoid型
template<typename T>
typename enable_if< /*条件式*/>::type func(T t)
{

}
template<typename T>
typename enable_if<!/*条件式*/>::type func(T t)
{

}
  • 返り値型が引数型に依存 その(1)
template<typename T>
typename enable_if< /*条件式*/,T>::type func(T t)
{

}
template<typename T>
typename enable_if<!/*条件式*/,T>::type func(T t)
{

}

enable_ifの第2型引数を指定すると、enable_if<>::typeがその型になる

  • 返り値型が引数型に依存 その(2)
template<typename T>
auto func(T t) -> typename enable_if< /*条件式*/,T>::type
{

}
template<typename T>
auto func(T t) -> typename enable_if<!/*条件式*/,T>::type
{

}

後置返り値型版

  • 返り値型が特定の型(例:int型) その(1) 関数の戻り値型を使う(1)
template<typename T>
typename enable_if< /*条件式*/,int>::type func(T t)
{

}
template<typename T>
typename enable_if<!/*条件式*/,int>::type func(T t)
{

}

enable_ifの第2型引数を指定すると、enable_if<>::typeがその型になる

  • 返り値型が特定の型(例:int型) その(1) 関数の戻り値型を使う(2)
template<typename T>
auto func(T t) -> typename enable_if< /*条件式*/,int>::type
{

}
template<typename T>
auto func(T t) -> typename enable_if<!/*条件式*/,int>::type
{

}

後置返り値型版

  • 返り値型が特定の型(例:int型) その(2)関数の引数型を使う (1)
template<typename T>
int func(T t, typename enable_if< /*条件式*/>::type = 0)
{

}
template<typename T>
int func(T t, typename enable_if<!/*条件式*/>::type = 0)
{

}

返り値型が使用できない場合、関数の第2引数の型を使用

  • 返り値型が特定の型(例:int型) その(2)関数の引数型を使う (2)
template<typename T>
void func(T t, typename enable_if< /*条件式*/>::type *= 0)
{

}
template<typename T>
void func(T t, typename enable_if<!/*条件式*/>::type *= 0)
{

}

ポインタ型版 こっちの方が良く見る

  • 返り値型が特定の型(例:int型) その(2)関数の引数型を使う (3)
template<typename T>
void func(T t, typename enable_if< /*条件式*/>::type *= nullptr)
{

}
template<typename T>
void func(T t, typename enable_if<!/*条件式*/>::type *= nullptr)
{

}

デフォルト引数がnullptr

引数が有限個のときにしか使用できない、などの制限があるため、
実際は次のTemplate型引数でチェックする方法が使われる(らしい)

  • 返り値型が特定の型(例:int型) その(3)テンプレートのデフォルト型引数を使う (駄目な例)

これはSFINAEが失敗します

template<typename T,
typename SFINAE = typename std::enable_if< /*条件式*/>::type >
void func(T t)
{

}

template<typename T,
typename SFINAE = typename std::enable_if<!/*条件式*/>::type >
void func(T t)
{

}

初見、なんでダメなのかわからなかったのですが デフォルト型引数にSFINAEは作用しないようです

  • 返り値型が特定の型(例:int型) その(4)テンプレートの型引数・・・ではなくてtemplate定数の型(int)を条件定義する (1)

テンプレートはtemplate<typename T>という書き方で、"型"をテンプレート引数にする以外に、
template<int N>と言う形で、"定数"をテンプレート引数にする事もできます
ですので、tempate<int N>func()func<42>という形で関数呼び出しできるわけです

このtemplate<int>intの部分をenable_ifの第2型引数を使って、間接的にintと書きます

template<typename std::enable_if</*hogehoge*/,int>::type=0> // template<int=0>
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                ~~~

これだとSHINAEが作用するそうです

template<typename T,
typename std::enable_if< /*条件式*/,int>::type = 0>
void func(T t)
{

}

template<typename T,
typename std::enable_if<!/*条件式*/,int>::type = 0> 
void func(T t)
{

}

enable_if<>::typeは第2型引数で与えた型、つまりintになります
しかし、enable_if<>::typeがintとなるのは、第一引数の条件が真のときだけのです
条件が偽ならば、template関数の形をなさなくなるので、オーバーロード解決時に
見送られます。よって条件が真のときだけオーバーロードされるのです

初めて見たとき、あまりに変態的な書き方で感動しました

  • 返り値型が特定の型(例:int型) その(4)テンプレートの型引数・・・ではなくてtemplate定数の型(int)を条件定義する (2)
extern void* enabler;


template<typename T,
typename std::enable_if< /*条件式*/>::type *& = enabler>
void func(T t)
{

}

template<typename T,
typename std::enable_if<!/*条件式*/>::type *& = enabler> 
void func(T t)
{

}

さらにまた一歩変態的な書き方になったバージョンです

私はこれに関しては、理解できていません
enablerはポインタ型のグローバル変数で、デフォルト型引数として・・・何を持っているのでしょう?

http://cpplover.blogspot.jp/2011/04/c0xenableif.html

  • 返り値型が特定の型(例:int型) その(4)テンプレートの型引数・・・ではなくてtemplate定数の型(int)を条件定義する (3)
template<typename T,
typename std::enable_if< /*条件式*/>::type * = nullptr>
void func(T t)
{

}

template<typename T,
typename std::enable_if<!/*条件式*/>::type * = nullptr> 
void func(T t)
{

}

上記のデフォルト型引数をnullptrに指定した事で
enablerをなくしたようです

http://d.hatena.ne.jp/gintenlabo/20110413/1302675301

  • 返り値型が特定の型(例:int型) その(5)decltypeを使用する (1)
template<typename T>
auto func(T t) -> decltype( typename std::enable_if< /*条件式*/>::type(), int() )
{

}

template<typename T> 
auto func(T t) -> decltype( typename std::enable_if<!/*条件式*/>::type(), int() )
{

}

decltype()の中にコンマ区切りで複数の式が入っていた場合、
結局は最後の式の型が返ってくると言う性質を利用したものです
decltype comma trickとか何とか呼ばれているやり方です
これの何がすばらしいかと言うと、decltypeの中に好きなだけand条件を書く事ができる点です

  • おまけ

オーバーロード解決のelse条件は以下のようにして書く事ができるようです

template<typename T>
auto func(T t) -> decltype( typename std::enable_if< /*条件式*/>::type(), int() )
{

}


auto func(...) -> decltype( int() )
{

}

可変個引数 (ellipsis)はオーバーロード解決時に、他のオーバーロード候補すべてにマッチしなかった場合に選択されます
したがって、2分岐のパターンマッチでは否定条件を定義するよりも、可変個引数を使用したほうがスマートのようです

http://code-mynote.blogspot.jp/2014/03/sfinae-ellipsis.html

ただリンク先にある型情報の消失に関して、まだ理解が無いため、本当に使うべきかは良くわかってないです・・・
(追記)
引数の数を制限して、2引数以上与えられた場合をそもそもコンパイル時にエラー検出したいときなんかは、使用できなさそうですね

(追記)
メンバ関数を有無を判定するhelperクラスを定義するときに使用されるようです。
http://cpplover.blogspot.jp/2010/03/decltypesfinae.html

引用元で実際に使用されている部分抜粋

template<typename T>
struct has_func
{
private :
    // check(x)呼び出しでxの型特性によって、戻り値型が変化する
    //
    //   SFINAE if(引数がfuncメンバ関数を持っている場合)
    template <typename U>
    static auto check(U x) -> decltype( x.func(), std::true_type() ) ; // true_typeを返す

    //   SFINAE else
    static auto check(...) -> decltype(           std::false_type() );  // false_typeを返す

public :
    //static bool const value = std::identity<decltype( check( value<T>() ) )>::type::value ;(*1)
    // (*1): std::identityが消滅したので書き換え
    static const bool value = decltype( check(T()) )::value  // 1) check()の返し値型がtrue_Type()->staticメンバ変数valueあり
                                                             //
                                                             // 2) check()の返し値型がfalse_Type()->staticメンバ変数valueなし->
                                                             //   エラーになる->(enable_if内でhas_func::valueを読んでいればSFINAE発動) 
} ;

書いてみた
http://melpon.org/wandbox/permlink/v1113biRVhKoG65g