C++0x の SFINAE で気づいたこと

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


C++0x の関数テンプレートがデフォルトテンプレート引数を取れるようになったことで可能になった,

extern void* enabler;

template < typename T,
  typename std::enable_if<
    std::is_arithmetic<T>::value
  >::type*& = enabler
>
void f( T ) {
  std::cout << "T is arithmetic" << std::endl;
}

template < typename T,
  typename std::enable_if<
    std::is_pointer<T>::value
  >::type*& = enabler
>
void f( T ) {
  std::cout << "T is pointer" << std::endl;
}

っていうコードって,イチイチ enabler を定義しなくても,

template < typename T,
  typename std::enable_if<
    std::is_arithmetic<T>::value
  >::type* = 0
>
void f( T ) {
  std::cout << "T is arithmetic" << std::endl;
}

template < typename T,
  typename std::enable_if<
    std::is_pointer<T>::value
  >::type* = 0
>
void f( T ) {
  std::cout << "T is pointer" << std::endl;
}

でよくね? って思ったので,ちょっと試してみた.


が,うまくいかない. http://ideone.com/c9atG


結論から言うと,規格の 14.3.2/5 によれば,このコードは ill-formed で意図したようには動作せず,正しくは

template < typename T,
  typename std::enable_if<
    std::is_arithmetic<T>::value
  >::type* = nullptr
>
void f( T ) {
  std::cout << "T is arithmetic" << std::endl;
}

template < typename T,
  typename std::enable_if<
    std::is_pointer<T>::value
  >::type* = nullptr
>
void f( T ) {
  std::cout << "T is pointer" << std::endl;
}

と書く必要があるみたいです. ( nullptr(void*)0 でも可.)
とはいえ,この動作は C++0x で追加された機能*1であり, GCC では未だ使えない*2為,
今でも問題なく使える enabler を使うのが一番な気もします.

最新の GCC 4.7 で実装されました! これで enabler なんて おさらばです.
…まぁ,正直, typename std::enable_if::type = 0 なら,今でも使えるんですけどね.