C++11テンプレート:クラスに指定した名前のメンバー変数があるかどうかをどのように判断しますか?

6088 ワード

クラスに指定されたメンバー関数があることをどのように判断するかは、ネット上で多くの文章を見つけることができます.例えば、次の2つの文章は詳しく書かれています.
《C++11の美》《C++テンプレート、メンバー関数が存在するかどうかを判断し、差別化操作を実現する》
私が今関心を持っているのは、クラスにメンバー変数があることをどのように判断するかです.メンバー変数は配列または他のクラスである可能性があります.上記のメンバー関数を判断する文章を見て、その原理を理解した後、メンバー変数かどうかを判断するのも、あまり差がない道理で、実現は非常に簡単です.
/*     ,  T     's'    * value  bool      * type s     (value true   ) */
template<typename T>
struct has_member_s{
    template <typename _T>
    static auto check(_T)->typename std::decay<decltype(_T::s)>::type;
    static void check(...);
    using type=decltype(check(std::declval()));
    enum{value=!std::is_void::value};
};

上記のテンプレートは、openclのcl_int2ベクトルタイプの例として、クラス内のsという名前のメンバーがあるかどうかを確認するために使用されます.以下はcl_int2の定義です.
/* ---- cl_intn ---- */
typedef union
{
    cl_int  CL_ALIGNED(8) s[2];
#if __CL_HAS_ANON_STRUCT__
   __CL_ANON_STRUCT__ struct{ cl_int  x, y; };
   __CL_ANON_STRUCT__ struct{ cl_int  s0, s1; };
   __CL_ANON_STRUCT__ struct{ cl_int  lo, hi; };
#endif
#if defined( __CL_INT2__) 
    __cl_int2     v2;
#endif
}cl_int2;
cl_int2にs配列という名前が表示されます.次はテストコードです.
#include 
#include 
int main(int argc, char * argv[]){
    cout<<"cl_int2="<::value<cout << "cl_int2=" << typeid(has_member_s::type).name() << endl;
    cout<<"int="<int>::value<//    
}

gccコンパイル運転結果
cl_int2=1 cl_int2=Pi int=0
vs 2015コンパイル実行結果
cl_int2=1 cl_int2=int * __ptr64 int=0
注:テンプレート関数の文
static auto check(_T)->typename std::decay(_T::s)>::type;
decltype(_T::s)はすでに_T::sのタイプを取得しており、std::decayでもう1階をカバーするのは余計なようですが、そうではありません.非配列メンバー変数については、std::decayのレイヤを削除し、直接
static auto check(_T)->decltype(_T::s);

完全に可能です(gccでもvs 2015でも).しかし配列タイプの変数については,上記の書き方ではgccでコンパイルは通過できるが,実行結果は誤りである.たぶんgccは返す値がint[2]のような配列ではなく,ポインタしかないと考えている.
static auto check(_T)->cl_int[2]; //   `std::decay` ,    ,  
static auto check(_T)->cl_int*; //   `std::decay` ,    ,  

このテンプレート関数を複数回用いて異なるメンバー変数を判断する必要がある場合,マクロを用いて上記のコードを改善する必要がある.
/*           ,  T     's'    * value  bool      * type s     (value true   ) */
#define has_member(s) \
template<typename T>\
struct has_member_##s{\
    template <typename _T>static auto check(_T)->typename std::decay<decltype(_T::s)>::type;\
    static void check(...);\
    using type=decltype(check(std::declval()));\
    enum{value=!std::is_void::value};\
};

このテンプレートをマクロとして定義した後、sメンバーがいるかどうかを確認するには、sをパラメータとしてhas_memberを展開します.
has_member(s);

xメンバーが存在するかどうかを確認するには、xをパラメータとしてhas_memberを展開します.
has_member(x);