3

編集:私自身の回答を投稿し、元の受け入れられた回答を保持しました...エイリアスについて考えさせられました。

編集:私の質問は、SFINAE(または他の)コンテキストでのメンバーvar/funcの存在とあいまいさを区別する可能性に向けられています。私の質問は、has_memberテンプレートを作成する方法ではなく、あいまいさと存在の違いを検出することです。

メンバーがあいまいな方法でアクセスされている場合(派生クラスの両方のベースにメンバーがある場合)と、メンバーがまったく存在しない場合(派生クラスのどちらのベースにもメンバーがない場合)を区別する部分的な特殊化を設定することは可能ですか? ?あいまいさが検出された場合にのみtrueを返す必要がありますが、メンバーがまったくない場合、またはメンバーが1つのクラスにのみ存在する場合は返しません。これは私がこれまでに持っているものであり、あいまいさ(私が欲しいもの)に対してtrueを返し、メンバーを持つ1つのクラス(私が望むもの)に対してのみfalseを返しますが、どちらのクラスにもメンバーがない場合はtrueを返します(argh!)

//for the sake of this example, ClassOne comes from a lib I don't control
struct ClassOne {
    //some unknown members in here... 
};

struct ClassTwo {
    string member_var;
};

template<typename A>
struct helper : std::true_type {};

template<typename A, typename B>
struct merged_class : public A, public B {};

template<typename T, typename = void>
struct has_member_var : std::true_type {
    //Member request ambiguous or neither class has member.
    //I want to catch these conditions separately,
    //but this one template catches both :(
    const int status = 1;
};

template<typename T>
struct has_member_var<
    T
    , typename std::enable_if<
        //The next line results in either ambiguous member request error
        //if both classes have the member OR
        //member does not exist error if neither class has the member
        //...how to tell the difference in the type of error?
        helper<decltype(T::member_var)>::value
        , T
    >::type
> : std::false_type {
    const int status = 2; //only one class has member 
};

//This next one I have no idea how to do, if it's even possible.
//I'd like a third specialization that will match either the
//ambiguous condition or the member only existing in one of the
//base classes.
template<typename T>
struct has_member<
    T
    , typename std::enable_if<
        some_freaky_magic<decltype(T::foo)>::true_value
        , T
    >::type
> : std::true_type {
    const int status = 3;
};

望ましい使用法:

switch(has_member<merged_class<ClassOne, ClassTwo>>::status) {
    case 1:
        cout << "member ambiguity";
        break;
    case 2:
        cout << "member non-existence";
        break;
    case 3:
        cout << "member existence for only one base";
        break;
}
4

3 に答える 3

6

了解しました。SFINAEという表現のアプローチを採用し、型の演繹を追加することができたと思います。これは非常に大雑把にハッキングされた答えであり、何か有用なことをしているように見えます(下部の使用例を参照してください)。

(プレゼンテーションはおそらくもっと簡潔でクリーンになる可能性がありますが、このようにすると、ステップに分割されていることがわかります。)

使用法: 両方にメンバーが呼び出されこのメンバーのタイプが異なる場合(厳密には、減衰されないconflicting_X<A, B>::value場合)にのみtrueになります。メンバーが両方のクラスに定義されているかどうかなどの中間の質問は、とで決定できます。ABx conflicting_X<A, B>::bothhas_X<T>::value

#include <iostream>
#include <type_traits>
#include <typeinfo>


// has_X is taken straight from the other topic

template <typename T>
struct has_X
{
  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;
};

// Here we go...

template <typename T>
struct XType
{
  typedef decltype(T::x) type;
};

template <bool, typename S, typename T>
struct compare_X
{
  static const bool value = false;
};

template <typename S, typename T>
struct compare_X<true, S, T>
{
  // Note that we don't decay, we want equality on the nose.
  static const bool value =  ! std::is_same<typename XType<S>::type, typename XType<T>::type>::value;
};

template <typename S, typename T>
struct conflicting_X
{
  // We split this up so that XType is only instantiated if T::x really exists.
  // You may also use conflicting_X::both as a useful datum.
  static const bool both = has_X<S>::value && has_X<T>::value;
  static const bool value = compare_X<both, S, T>::value;
};


/*** Example ***/

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

void f(double) { }

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

  std::cout << "Conflict A/B? " << conflicting_X<A, B>::value << std::endl;
  std::cout << "Conflict A/C? " << conflicting_X<A, C>::value << std::endl;
}
于 2011-06-09T00:31:40.570 に答える
2

更新:最近、元の回答に投稿したコードでさらにいくつかの作業を行ったので、変更/追加を考慮してこれを更新しています。

ここにいくつかの使用法の抜粋があります:*これらすべての根性はさらに下にあります

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;

メンバー列挙型を確認し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;

また

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)
于 2011-06-12T21:11:39.017 に答える
1

これは不可能だと思います。SFINAEがここで機能するには、探しているメンバーがあいまいな場合にのみ、特定の有効な/形式の悪い組み合わせである1つ以上の式が必要です。これは、C++0xの拡張SFINAEを想定しています。

ただし、タイプTと潜在的な(タイプ以外の)メンバーが与えられiた場合、使用可能な式は何ですか?また、ケースをデータメンバーのみに&T::i制限する場合、タイプはです。t.itT

どちらの式でも、iあいまいな場合はそれぞれの形式が正しくありませんが、存在し、あいまいでない場合はそれぞれの形式が適切です。そのため、お客様の要件を満たすことはできません。

B明確なデータメンバーを持つ潜在的なベースが与えられた場合、とでiテストできることに注意してください。最初の式の形式が正しくなく、2番目の式の形式が正しい場合、これはあいまいなメンバーが存在することを意味します。それは私があなたの要求に達することができる最も近いものです。&T::it.B::ii

于 2011-06-08T22:55:14.230 に答える