メンバー関数の存在を確かめる方法

Dart vs JSX の記事がうまく書けないので、今回はC++11ネタでお茶を濁したいと思います。 C++11では型周りを扱うコードをC++03などに比べるとかなり簡単に書けるようになりました("C++ SFINAE" 等で検索すると良いです)ので、今回は"型Tがメンバ関数fooを持っているかどうかを確かめるクラス"を紹介します。 少し既出感があるかもしれません。


#include <type_traits>
#include <utility>

template<typename T>
class has_foo
{
private:
    template<typename U>
    static auto check( U v ) -> decltype( v.foo(), std::true_type() );
    static auto check( ... ) -> decltype( std::false_type() );

public:
    typedef decltype( check( std::declval<T>() ) ) type;
    static bool const value = type::value;
};


class klass
{
public:
    void foo() const {}
};


#include <iostream>

int main()
{
    std::cout
        << has_foo<int>::value << std::endl
        << has_foo<klass>::value << std::endl;

}

なんと!たったのこれだけで判別することができるのです。 (http://ideone.com/ptYMj)

ポイントは8行目の


static auto check( U v ) -> decltype( v.foo(), std::true_type() );

です。 operator, の特徴を利用して、v.foo()が有効な式であれば、std::true_typeを返すようになっています。 また、v.foo()が有効な式でなければ(メンバ関数foo()が存在しなければ)、SFINAEによって下の関数が使われるため、std::false_typeを返します。 ちなみにoperator, の動作はこんな感じです。


#include <iostream>
#include <vector>
#include <string>

int main()
{
  std::cout
    << ( 72, std::vector<int>(), std::string( "foooo!" ) ) << std::endl;
}

(http://ideone.com/ebRBb)

欠点としては、この際に class klass が operator,( std::true_type ) を意図しない形で実装していた場合などにコンパイルエラーになるという事です。 その解決策は以前に教えて頂いたことがあり、との時のまとめがあるので是非御覧ください! http://togetter.com/li/38905

結論 C++11は便利奥深い。