2009-08-07

C++0xのすんばらしいSFINAE

 template<typename C> auto begin(C& c) -> decltype(c.begin());
template<typename C> auto begin(const C& c) -> decltype(c.begin());

上記の関数は、cがbegin()メンバ関数を持っているか、const修飾されたbegin()メンバ関数を持っているかどうかで、SFINAEによって適切なOverload Resolutionがなされるらしい。知らなかった。

ということはだ。この新しい関数宣言とdecltypeのテクニックを使えば、型がある名前のメンバ変数やメンバ関数を持っているかどうかを調べるメタ関数が、簡単にかけることになる。

#include <type_traits>

// T型がhogeというメンバ変数を持っているかどうかを判別するメタ関数
template < typename T >
struct has_mem_hoge
{
private :

    template < typename U >
    [] check() -> decltype(reinterpret_cast<U *>(nullptr)->hoge, std::true_type) ;
    template < typename U >
    std::false_type check() ;

public :
    static const bool value = std::is_same< check<T>(), std::true_type >::value ;
} ;

// T型がhoge()というメンバ変数を持っているかどうか判別するメタ関数
template < typename T >
struct has_memfun_hoge
{
private :

    template < typename U >
    [] check() -> decltype(reinterpret_cast<U *>(nullptr)->hoge(), std::true_type) ;
    template < typename U >
    std::false_type check() ;

public :
    static const bool value = std::is_same< check<T>(), std::true_type >::value ;
} ;

ところで、Variadic Templateは、いまいち分かっていないのだが、もし上記のhas_memfun_hogeを、任意の数の引数をもつメンバ関数hoge()に対応させるには、こんな風に書けばいいのだろうか。

template < typename T >
struct has_memfun_hoge
{
private :

    template < typename U, typename ... Types>
    [] check() -> decltype(reinterpret_cast<U *>(nullptr)->hoge(Types ... args), std::true_type) ;
    template < typename U >
    std::false_type check() ;

public :
    static const bool value = std::is_same< check<T>(), std::true_type >::value ;
} ;

あと、良く考えたら、これでもいいよね。

//面倒
std::is_same< check<T>(), std::true_type >::value ;
//簡単
decltype( check<T>() )::value

1 comment:

les said...

最近プログラミングを勉強し始めたもので、いつも参考にさせて頂いています。
5年も前の記事にコメントというのもなんなのですが、引数を指定すると少し不自由があるかもしれませんね。
intとdoubleなど、暗黙の変換が可能であれば意図しない結果が帰ってくることがありそうです。

template // 第二引数以降は特定のメンバ関数の引数の指定
struct has_hoge
{
private:
template
static auto confirm( U u, Args... args ) -> decltype( u.hoge( args... ), std::true_type() );
static std::false_type confirm( ... );
public:
enum : bool { value = decltype( confirm( std::declval(), std::declval()... ) )::value };
};

struct hage { void hoge( double ); };
struct huge { template void hoge( T ); };

int main()
{
has_hoge::value; // true
has_hoge::value; // true
}