6

member_func次のコードは、コンパイル時に型 (基本的にはクラス) にメンバー関数が含まれているかどうかを確認する SFINAE 実装を示しています。

#define CHECKER(func_name,class_name) sizeof(class_name<T>::template func_name<T>(0)) == 1
#include <iostream>
struct A
{
    void member_func();
};
struct B {};
template<typename T>struct Check_If_T_Is_Class_Type
{
    template<typename C> static char func (char C::*p);
    template<typename C> static long func (...);
    enum{val = CHECKER(func,Check_If_T_Is_Class_Type)};
};

//APPROACH 1
template <typename T>struct TypeHasMemberFunc
{
    template <typename C, C> struct TypeCheck;
    template <typename C> struct Prototype_Holder {typedef void (C::*fptr)();};
    template <typename C> static char func(TypeCheck
                                           <
                                              typename Prototype_Holder<C>::fptr,
                                              &C::member_func
                                           >*);
    template <typename C> static long func(...);
    enum {value = CHECKER(func,TypeHasMemberFunc)};
};

//APPROACH 2
template <typename T>struct has_member_func
{
    template<class C> static char func(char (*)[sizeof(&C::member_func)]);
    template<class C> static long func(...);
    enum{value = CHECKER(func,has_member_func)};
};
int main(){
 if(Check_If_T_Is_Class_Type<A>::val)
   std::cout<<TypeHasMemberFunc<A>::value; //using APPROACH 1

 if(Check_If_T_Is_Class_Type<B>::val)
   std::cout<<has_member_func<B>::value; //using APPROACH 2
}

ただし、私の質問は、どちらのアプローチを好むか (アプローチ 1 またはアプローチ 2) とその理由です。
与えられたアプローチに矛盾はありますか? はいの場合はお知らせください。

PS:仮定sizeof(char)!= sizeof(long)

4

3 に答える 3

1

2 番目のアプローチは、関数の型 (戻り値の型または引数の型) をチェックせず、クラスの型だけでなく、すべての型で機能します。

于 2010-12-05T16:40:34.117 に答える
0

個人的には、2 番目のアプローチの方が短くて理解しやすいため、2 番目のアプローチを使用することをお勧めします。しかし、GCC はそれをコンパイルしないため、GCC には次のようなものを使用する必要があります。

namespace detail
{
    template<class C> char func(char (*)[sizeof(&C::member_func)]);
    template<class C> long func(...);   
}

template <typename T>struct has_member_func
{
    enum{value = (sizeof(detail::func<T>(0)) == 1)};
};

また、CHECKER マクロをなくした方がいいです。コードが非常に読みにくくなります。

とにかく、本番コードでそのような C++ ハックを使用することは控えます (ただし、Boost チームのメンバーである場合を除きます :-)

このようなものはエラーが発生しやすく、サポートが難しく、コンパイラ間での移植が困難ですが、主なポイントは、そのようなハードコード C++ を必要とする実際のタスクを覚えていないということです。

于 2010-12-05T19:01:22.537 に答える
-1

EDIT : 完了および修正。

継承のあいまいさを使用する別のアプローチは、おそらくアプローチ 2 と機能的に同等です。アプローチ 1 で問題が発生したことを覚えています (ただし、G++ 4.4.5 でコンパイルされます)。私は頼らなければなりませんでした:

template <class T>
struct has_foo
{
  struct fallback { void foo(...); };
  struct D : T, fallback { };

  template <typename U, U> struct K;

  // Will be ambiguous for U = D iff T has a foo member function.                                                                                                         
  // It doesn't trigger an error, since D will always have at least one                                                                                                   
  // foo member function.                                                                                                                                                 
  template <class U> static char (&test(K<void (fallback::*)(...), &U::foo>*))[1];
  template <class U> static char (&test(...))[2];

  static const bool value = sizeof(test<D>(0)) == 2;
};

これは T がクラスの場合に機能するため、T がクラス型かどうかを確認するためのレイヤーを追加することをお勧めします。

fooすべてのメンバー関数が検出されることに注意してください。foo検出された関数が指定された引数で呼び出せるかどうかを確認したい場合は、SFINAE の別のレイヤーを実行する必要があります。

// Check whether foo can be called with an argument of type Arg
// and yields an element of type Res.
// If you need Res = void, this code does not work.
template <class T, typename Arg, typename Res>
struct check_foo
{
    struct flag {};
    struct D : T { using T::foo; flag foo(...); };

    template <typename U>
    static char (&test(U))[1];

    template <typename> static char (&test(...))[2];

    static Arg f();

    static const bool value = sizeof(test<Arg>( ((D*)0)->foo(f()) )) == 1;
};
于 2010-12-05T11:46:14.990 に答える