web-dev-qa-db-ja.com

クラスに特定のメンバー変数があるかどうかを検出する方法は?

アルゴリズムテンプレート関数を作成するには、テンプレート引数であるクラスのxまたはX(およびyまたはY)を知る必要があります。 MFC CPointクラス、GDI + PointFクラス、またはその他の関数に自分の関数を使用するときに役立ちます。それらはすべて、異なるxを使用しています。私の解決策は次のコードに減らすことができます:


template<int> struct TT {typedef int type;};
template<class P> bool Check_x(P p, typename TT<sizeof(&P::x)>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<sizeof(&P::X)>::type b = 0) { return false; }

struct P1 {int x; };
struct P2 {float X; };
// it also could be struct P3 {unknown_type X; };

int main()
{
    P1 p1 = {1};
    P2 p2 = {1};

    Check_x(p1); // must return true
    Check_x(p2); // must return false

    return 0;
}

ただし、GNU C++でコンパイルしている間、Visual Studioではコンパイルしません。VisualStudioでは、次のテンプレートを使用できます。


template<class P> bool Check_x(P p, typename TT<&P::x==&P::x>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<&P::X==&P::X>::type b = 0) { return false; }

しかし、GNU C++でコンパイルされません。普遍的な解決策はありますか?

UPD:ここでの構造P1とP2は例にすぎません。メンバーが不明なクラスが存在する可能性があります。

追伸C++ 11ソリューションは明らかであり、質問に関連しないため、ここに投稿しないでください。

59

別の方法はこれです 式のSFINAE にも依存します。名前検索の結果があいまいな場合、コンパイラはテンプレートを拒否します

template<typename T> struct HasX { 
    struct Fallback { int x; }; // introduce member name "x"
    struct Derived : T, Fallback { };

    template<typename C, C> struct ChT; 

    template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1]; 
    template<typename C> static char (&f(...))[2]; 

    static bool const value = sizeof(f<Derived>(0)) == 2;
}; 

struct A { int x; };
struct B { int X; };

int main() { 
    std::cout << HasX<A>::value << std::endl; // 1
    std::cout << HasX<B>::value << std::endl; // 0
}

Usenetの誰かの素晴らしいアイデアに基づいています。

注:HasXは、任意のタイプのデータまたは関数メンバーxをチェックします。メンバー名を紹介する唯一の目的は、メンバー名の検索にあいまいさを持たせることです-メンバーのタイプは重要ではありません。

Johannes Schaub-litbone よりも簡単なソリューションを次に示します。 C++ 11が必要です。

_#include <type_traits>

template <typename T, typename = int>
struct HasX : std::false_type { };

template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };
_

Update:簡単な例と、この仕組みの説明。

これらのタイプの場合:

_struct A { int x; };
struct B { int y; };
_

_HasX<A>::value == true_と_HasX<B>::value == false_があります。理由を見てみましょう。

最初に、_std::false_type_および_std::true_type_には、それぞれvalueおよびfalseに設定されるtrueという名前の_static constexpr bool_メンバーがあることを思い出してください。したがって、上記の2つのテンプレートHasXはこのメンバーを継承します。 (_std::false_type_の最初のテンプレートと_std::true_type_の2番目のテンプレート。)

簡単に始めてから、上記のコードに到達するまで段階的に進めましょう。

1)出発点:

_template <typename T, typename U>
struct HasX : std::false_type { };
_

この場合、驚きはありません:HasXは_std::false_type_から派生し、したがって_HasX<bool, double>::value == false_および_HasX<bool, int>::value == false_から派生します。

2)Uのデフォルト設定:

_// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };
_

Uのデフォルトがintであるとすると、_Has<bool>_は実際には_HasX<bool, int>_を意味し、したがって_HasX<bool>::value == HasX<bool, int>::value == false_を意味します。

3)専門分野の追加:

_// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

// Specialization for U = int
template <typename T>
struct HasX<T, int> : std::true_type { };
_

一般に、プライマリテンプレートのおかげで、_HasX<T, U>_は_std::false_type_から派生します。ただし、_U = int_から派生した_std::true_type_の特殊化が存在します。したがって、_HasX<bool, double>::value == false_が_HasX<bool, int>::value == true_です。

Uのデフォルトのおかげで、_HasX<bool>::value == HasX<bool, int>::value == true_。

4)decltypeおよびintを言うための派手な方法:

ここで少し余談しますが、どうか、我慢してください。

基本的に(これは完全に正しいわけではありません)、decltype(expression)expressionのタイプを生成します。たとえば、_0_はint型であるため、decltype(0)intを意味します。同様に、_1.2_の型はdoubleであるため、decltype(1.2)doubleを意味します。

次の宣言を持つ関数を考えてみましょう。

_char func(foo, int);
_

ここで、fooは何らかのクラスタイプです。 ffoo型のオブジェクトである場合、decltype(func(f, 0))charfunc(f, 0)によって返される型)を意味します。

現在、式_(1.2, 0)_は、2つの部分式を順番に評価する(組み込み)コンマ演算子を使用します(つまり、最初の_1.2_、次に_0_)、最初の値を破棄します2番目の結果になります。したがって、

_int x = (1.2, 0);
_

に等しい

_int x = 0;
_

これをdecltypeと組み合わせると、decltype(1.2, 0)intを意味します。ここで_1.2_またはdoubleについて特別なことは何もありません。たとえば、trueの型はboolであり、decltype(true, 0)intも意味します。

クラス型はどうですか?インスタンスの場合、decltype(f, 0)はどういう意味ですか?これがまだintを意味することを期待するのは自然ですが、そうではないかもしれません。実際、funcfooを取り、intを返す上記の関数charに似たコンマ演算子のオーバーロードが存在する場合があります。この場合、decltype(foo, 0)charです。

コンマ演算子のオーバーロードの使用をどのように回避できますか?まあ、voidオペランドのコンマ演算子をオーバーロードする方法はなく、何でもvoidにキャストできます。したがって、decltype((void) f, 0)intを意味します。実際、_(void) f_はffooからvoidにキャストしますが、これは基本的に、式がvoid型を持っていると見なさなければならないことを示すだけです。次に、組み込み演算子のコンマが使用され、_((void) f, 0)_はint型の_0_になります。したがって、decltype((void) f, 0)intを意味します。

このキャストは本当に必要ですか?まあ、foointをとるコンマ演算子のオーバーロードがなければ、これは必要ありません。ソースコードをいつでも調べて、そのような演算子があるかどうかを確認できます。ただし、これがテンプレートに表示され、fの型パラメーターがVである場合、そのようなコンマ演算子のオーバーロードが存在するかどうかは明確ではありません(または知ることさえできません)。とにかく一般的にするためにキャストします。

一番下の行:decltype((void) f, 0)は、intを言うのに凝った方法です。

5)SFINAE:

これは完全な科学です;-) OK私は大げさですが、それは非常に単純でもありません。そのため、説明は最小限に留めます。

SFINAEはSubstitution Failure is Not Errorの略です。テンプレートパラメーターが型で置換されると、不正なC++コードが表示される可能性がありますが、いくつかの状況では、コンパイルを中止する代わりに、コンパイラーは単に違反コードをあたかも無視しますありませんでした。それが私たちのケースにどのように適用されるか見てみましょう:

_// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

// Specialization for U = int
template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };
_

ここでも、decltype((void) T::x, 0)intを言うのに派手な方法ですが、SFINAEの利点があります。

Tが型に置換されると、無効な構成体が表示される場合があります。たとえば、_bool::x_は有効なC++ではないため、_T::x_でTboolに置き換えると、無効な構文が生成されます。 SFINAEの原則では、コンパイラはコードを拒否せず、単に(の一部を)無視します。より正確には、見たように_HasX<bool>_は実際には_HasX<bool, int>_を意味します。 _U = int_の特殊化を選択する必要がありますが、インスタンス化中にコンパイラーは_bool::x_を検出し、テンプレートの特殊化が存在しないかのように完全に無視します。

この時点で、コードは本質的に上記のケース(2)と同じであり、プライマリテンプレートのみが存在します。したがって、_HasX<bool, int>::value == false_。

_B::x_は無効な構造であるため、boolに使用されるのと同じ引数がBに適用されます(Bにはメンバーxがありません)。ただし、_A::x_はOKであり、_U = int_(より正確には、U = decltype((void) A::x, 0))の特殊化をインスタンス化する際にコンパイラーは問題を認識しません。したがって、_HasX<A>::value == true_。

6)Uの名前の変更:

さて、(5)のコードをもう一度見ると、名前Uは宣言(_typename U_)以外では使用されていないことがわかります。次に、2番目のテンプレート引数の名前を解除し、この投稿の上部に示されているコードを取得します。

80
Cassio Neri

私はここからリダイレクトされました question これはこれの複製として閉じられました。古いスレッドであることは知っていますが、C++ 11で動作する代替(単純な)実装を提案したかっただけです。特定のクラスにidというメンバー変数があるかどうかを確認するとします。

#include <type_traits>

template<typename T, typename = void>
struct has_id : std::false_type { };

template<typename T>
struct has_id<T, decltype(std::declval<T>().id, void())> : std::true_type { };

それでおしまい。そして、これがどのように使用されるかです( live example ):

#include <iostream>

using namespace std;

struct X { int id; };
struct Y { int foo; };

int main()
{
    cout << boolalpha;
    cout << has_id<X>::value << endl;
    cout << has_id<Y>::value << endl;
}

いくつかのマクロを使用すると、さらに簡単になります。

#define DEFINE_MEMBER_CHECKER(member) \
    template<typename T, typename V = bool> \
    struct has_ ## member : false_type { }; \
    template<typename T> \
    struct has_ ## member<T, \
        typename enable_if< \
            !is_same<decltype(declval<T>().member), void>::value, \
            bool \
            >::type \
        > : true_type { };

#define HAS_MEMBER(C, member) \
    has_ ## member<C>::value

この方法で使用できます:

using namespace std;

struct X { int id; };
struct Y { int foo; };

DEFINE_MEMBER_CHECKER(foo)

int main()
{
    cout << boolalpha;
    cout << HAS_MEMBER(X, foo) << endl;
    cout << HAS_MEMBER(Y, foo) << endl;
}
30
Andy Prowl

更新:最近、元の回答に投稿したコードをさらに使用したため、変更/追加を考慮してこれを更新しています。

使用方法のスニペットを次に示します。*これらすべての根性はさらに下にあります

指定されたクラスのメンバーxを確認します。var、func、class、union、enumのいずれかです:

_CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;
_

メンバー関数の確認void x()

_//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;
_

メンバー変数x:を確認

_CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;
_

メンバークラスx:を確認

_CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;
_

メンバーユニオンx:を確認

_CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;
_

メンバーenum x:を確認

_CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;
_

署名に関係なく、メンバー関数xを確認します:

_CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;
_

OR

_CREATE_MEMBER_CHECKS(x);  //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;
_

詳細とコア:

_/*
    - Multiple inheritance forces ambiguity of member names.
    - SFINAE is used to make aliases to member names.
    - Expression SFINAE is used in just one generic has_member that can accept
      any alias we pass it.
*/

template <typename... Args> struct ambiguate : public Args... {};

template<typename A, typename = void>
struct got_type : std::false_type {};

template<typename A>
struct got_type<A> : std::true_type {
    typedef A type;
};

template<typename T, T>
struct sig_check : std::true_type {};

template<typename Alias, typename AmbiguitySeed>
struct has_member {
    template<typename C> static char ((&f(decltype(&C::value))))[1];
    template<typename C> static char ((&f(...)))[2];

    //Make sure the member name is consistently spelled the same.
    static_assert(
        (sizeof(f<AmbiguitySeed>(0)) == 1)
        , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
    );

    static bool const value = sizeof(f<Alias>(0)) == 2;
};
_

マクロス(El Diablo!):

CREATE_MEMBER_CHECK:

_//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member)                                         \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct Alias_##member;                                                      \
                                                                            \
template<typename T>                                                        \
struct Alias_##member <                                                     \
    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value>  \
> { static const decltype(&T::member) value; };                             \
                                                                            \
struct AmbiguitySeed_##member { char member; };                             \
                                                                            \
template<typename T>                                                        \
struct has_member_##member {                                                \
    static const bool value                                                 \
        = has_member<                                                       \
            Alias_##member<ambiguate<T, AmbiguitySeed_##member>>            \
            , Alias_##member<AmbiguitySeed_##member>                        \
        >::value                                                            \
    ;                                                                       \
}
_

CREATE_MEMBER_VAR_CHECK:

_//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name)                                   \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_var_##var_name : std::false_type {};                      \
                                                                            \
template<typename T>                                                        \
struct has_member_var_##var_name<                                           \
    T                                                                       \
    , std::integral_constant<                                               \
        bool                                                                \
        , !std::is_member_function_pointer<decltype(&T::var_name)>::value   \
    >                                                                       \
> : std::true_type {}
_

CREATE_MEMBER_FUNC_SIG_CHECK:

_//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix)    \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_func_##templ_postfix : std::false_type {};                \
                                                                            \
template<typename T>                                                        \
struct has_member_func_##templ_postfix<                                     \
    T, std::integral_constant<                                              \
        bool                                                                \
        , sig_check<func_sig, &T::func_name>::value                         \
    >                                                                       \
> : std::true_type {}
_

CREATE_MEMBER_CLASS_CHECK:

_//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_class_##class_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_class_##class_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_class<                                    \
            typename got_type<typename T::class_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}
_

CREATE_MEMBER_UNION_CHECK:

_//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_union_##union_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_union_##union_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_union<                                    \
            typename got_type<typename T::union_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}
_

CREATE_MEMBER_ENUM_CHECK:

_//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name)                 \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_enum_##enum_name : std::false_type {};    \
                                                            \
template<typename T>                                        \
struct has_member_enum_##enum_name<                         \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_enum<                                     \
            typename got_type<typename T::enum_name>::type  \
        >::value                                            \
    >                                                       \
> : std::true_type {}
_

CREATE_MEMBER_FUNC_CHECK:

_//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func)          \
template<typename T>                            \
struct has_member_func_##func {                 \
    static const bool value                     \
        = has_member_##func<T>::value           \
        && !has_member_var_##func<T>::value     \
        && !has_member_class_##func<T>::value   \
        && !has_member_union_##func<T>::value   \
        && !has_member_enum_##func<T>::value    \
    ;                                           \
}
_

CREATE_MEMBER_CHECKS:

_//Create all the checks for one member.  Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member)    \
CREATE_MEMBER_CHECK(member);            \
CREATE_MEMBER_VAR_CHECK(member);        \
CREATE_MEMBER_CLASS_CHECK(member);      \
CREATE_MEMBER_UNION_CHECK(member);      \
CREATE_MEMBER_ENUM_CHECK(member);       \
CREATE_MEMBER_FUNC_CHECK(member)
_
7
Brett Rossier

Boost.ConceptTraits は、他の間に型特性を定義するためのいくつかのマクロを提供します。たとえば、次の形式の型特性を定義するBOOST_TT_EXT_DEFINE_HAS_MEMBER(name)です。

has_member_##name<T>

Tにという名前のメンバータイプがある場合、これはtrueになります。ただし、これは参照型のメンバーを検出しないことに注意してください。

あなたの場合、ヘッダーファイルに追加するだけで十分です

BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x)

そして、次のように確認してください

BOOST_STATIC_ASSERT(has_member_x<P1>::value);

使用される手法は、前述の回答のいくつかで説明したものと同じです。

残念ながら、このライブラリはメンテナンスされていません。 C++ 0xにはコンセプトが含まれないため、このライブラリとSFINAEを組み合わせることで、ほとんどのコンセプトに対応できます。

これに対する2番目の回答(litb's)は、メンバーを検出する方法を示しています。

関数の存在をチェックするテンプレートを書くことは可能ですか?

2
James Hopkin

このような専門化を使用してみませんか:

struct P1 {int x; };
struct P2 {int X; };

template<class P> 
bool Check_x(P p) { return true; }

template<> 
bool Check_x<P2>(P2 p) { return false; }
2
Naveen

Check_xのテンプレート特化を作成してみませんか?

template<> bool Check_x(P1 p) { return true; }
template<> bool Check_x(P2 p) { return false; }

ヘック、私がそれを考えるとき。タイプが2つしかない場合、なぜこのためにテンプレートが必要なのですか?

1
ralphtheninja

関数(x、X、y、Y)は抽象基本クラスからのものですか、それともそうするようにリファクタリングできますか?その場合、Modern C++ DesignのSUPERSUBCLASS()マクロを、この質問に対する回答のアイデアとともに使用できます。

コンパイル時タイプベースのディスパッチ

1
user23167

コンパイル時に取得できます:0 - not_member, 1 - is_object, 2 - is_function必要なクラスとメンバーごとに-オブジェクトまたは関数: http://ideone.com/Fjm9u5

#include <iostream>
#include <type_traits>

#define IS_MEMBER(T1, M)    \
struct {        \
    struct verystrangename1 { bool M; };    \
    template<typename T> struct verystrangename2 : verystrangename1, public T { }; \
    \
    enum return_t { not_member, is_object, is_function }; \
    template<typename T, typename = decltype(verystrangename2<T>::M)> constexpr return_t what_member() { return not_member;  }  \
    template<typename T> typename std::enable_if<std::is_member_object_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_object; }   \
    template<typename T> typename std::enable_if<std::is_member_function_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_function; }   \
    constexpr operator return_t() { return what_member<T1>(); } \
}

struct t {
    int aaa;
    float bbb;
    void func() {}
};

// Can't be in function
IS_MEMBER(t, aaa) is_aaa_member_of_t;
IS_MEMBER(t, ccc) is_ccc_member_of_t;
IS_MEMBER(t, func) is_func_member_of_t;

// known at compile time
enum { const_is_aaa_member_of_t = (int)is_aaa_member_of_t };
static constexpr int const_is_func_member_of_t = is_func_member_of_t;

int main() {        
    std::cout << std::boolalpha << "0 - not_member, 1 - is_object, 2 - is_function \n\n" <<
        "is aaa member of t = " << is_aaa_member_of_t << std::endl << 
        "is ccc member of t = " << is_ccc_member_of_t << std::endl << 
        "is func member of t = " << is_func_member_of_t << std::endl << 
        std::endl;

    return 0;
}

結果:

0 - not_member, 1 - is_object, 2 - is_function 

is aaa member of t = 1
is ccc member of t = 0
is func member of t = 2

クラス/構造の場合:

struct t {
    int aaa;
    float bbb;
    void func() {}
};
0
Alex