ProgramingTip

클래스에 특정 멤버 변수가 있는지 여부를 감지하는 방법은 무엇입니까?

bestdevel 2020. 12. 6. 21:54
반응형

클래스에 특정 멤버 변수가 있는지 여부를 감지하는 방법은 무엇입니까?


알고리즘 템플릿 함수를 만들려면 템플릿 인수 인 클래스에서 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에서 발견하지 않습니다. Visual Studio에서는 다음 템플릿을 사용할 수 있습니다.


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 솔루션은 분명하고 질문과 관련이 없기 때문에 여기에 게시하지 않습니다.


또 다른 방법은 표현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
}

유즈넷의 누군가에 대한 훌륭한 아이디어를 기반으로합니다.

참고 : HasX는 임의의 유형으로 x라는 데이터 또는 함수 멤버를 확인합니다. 멤버 이름을 소개하는 유일한 목적은 멤버 이름 조회에 대한 모호성을위한 것입니다. 멤버 유형은 중요하지 않습니다.


여기에보다 솔루션 간단 요하네스 SCHAUB-litb하나 . 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 { };

업데이트 : 간단한 예와 작동 원리에 대한 설명입니다.

이러한 유형의 경우 :

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

우리는 HasX<A>::value == trueHasX<B>::value == false. 이유를 봅시다.

우선 거기 std::false_type하고 std::true_type있다 static constexpr bool라는 부재 value로 설정 false하고 true, 각각을,. 따라서 HasX의 두 템플릿 은이 멤버를 상속합니다. (의 첫 번째 템플릿 std::false_type과의 두 번째 템플릿 std::true_type.)

간단하게 다음 위의 코드에 도달 할 때까지 단계별로 진행하겠습니다.

1) 출발점 :

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

이 경우, 놀랄 경우 : HasX에서 std::false_type파생 HasX<bool, double>::value == falseHasX<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) 유형을 생성합니다 . 예를 들어 유형은 은를 의미 합니다. 유사하게, 입력 보유하고 , 수단 수단 .0intdecltype(0)int1.2doubledecltype(1.2)double

다음 선언이있는 함수를 고려하십시오.

char func(foo, int);

foo클래스 유형은 어디에 있습니까? 경우 f유형의 오브젝트는 foo, 다음 decltype(func(f, 0))수단 char(의해 반환 타입 func(f, 0)).

이제 사용 (1.2, 0)은 두 개의 하위 하위 사용을 순서대로 (즉, 먼저 1.2와 다음 0) 평가하는 (내장) 쉼표 연산자를 사용 하고 첫 번째 값을 버리고 두 번째 값을 생성합니다. 그 후,

int x = (1.2, 0);

다음과 같다

int x = 0;

이것과 함께 퍼팅 decltype그 제공 decltype(1.2, 0)수단 int. 정말 특별한 것은 없습니다 1.2또는 double여기가. 예를 들어, true유형 booldecltype(true, 0)수단 int도 있습니다.

클래스 유형은 어떻습니까? instace의 경우 decltype(f, 0)무엇 의미합니까? 그것이 여전히 의미가 예상되는 것은 당연 할 수 int있습니다. 실제로, 을 취하고를 반환하는 의 함수와 유사한 쉼표 연산자에, 대한 over-로드가있을 수 있습니다 . 이 경우 입니다 .funcfoointchardecltype(foo, 0)char

쉼표 연산자에 대한 향상된 사용을 어떻게 피할 수 있습니까? 음, void피연산자에 대해 쉼표 연산자를 오버로드 할 수있는 방법이 무엇 이든 void. 따라서 decltype((void) f, 0)의미 int합니다. 실제로 (void) f캐스트 f에서 foovoid이는 기본적으로 아무것도하지 않습니다하지만 표현이 유형을 가진 것으로 간주해야 우리한다는 말 void. 그런 다음 내장 연산자 쉼표가 일반적 ((void) f, 0)결과 0유형은 int. 따라서 decltype((void) f, 0)수단 int.

이 캐스트가 정말 필요한가요? 이 쉼표 연산자는 더 많이 사용하지 않는 경우에는 음, foo그리고 int다음이 필요하지 않습니다. 유효한 연산자가 있는지 여부를 확인하기 위해 항상 소스 코드를 검사 할 수 있습니다. 그러나 이것이 바로 템플릿에 나타나고 템플릿 변수 인 f유형 V이있는 경우, 쉼표 연산자에 대한 오버로드가 존재하는지 여부가 더 이상 명확하지 않습니다. 일반적으로 우리는 캐스팅합니다.

결론 : decltype((void) f, 0)은 멋진 표현 int입니다.

5) SFINAE :

이것은 전체 과학입니다 ;-) 좋아 나는 과장하고 그것도 매우 간단하지 않습니다. 최소한 설명은 최소한 유지하겠습니다.

SFINAE는 대체 실패는 오류가 아닙니다. 즉, 템플릿 매개 변수가 유형으로 대체되면 잘못된 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 { };

다시 말하지만 SFINAE의 이점 decltype((void) T::x, 0)이있는 멋진 int말입니다.

T가 유형으로 대체 잘못된 구성이 나타날 수 있습니다. 예를 들어 bool::x는 유효한 C ++가 아니므 Tboolin으로 대체 T::x하면 잘못된 구문이 생성됩니다. SFINAE 원칙에 따라 컴파일러는 코드를 거부하지 않고 무시합니다. 보다 정확하게는 우리가 본 것처럼 HasX<bool>실제로 HasX<bool, int>. 에 대한 전문화를 U = int선택해야하지만 인스턴스화하는 동안 컴파일러는 bool::x템플릿 전문화가 존재하지 않는 것처럼 모두 찾아서 무시합니다.

이 시점에서 코드는 기본 템플릿 만 존재하는 위의 경우 (2)와 동일합니다. 따라서 HasX<bool, int>::value == false.

이후에 대한 bool보류에 사용 된 동일한 인수 는 유효하지 않은 구성입니다 ( 멤버 없음 ). 그러나 괜찮은 컴파일러는에 대한 전문화를 인스턴스화하는 데 많은 문제가 없습니다 (또는 더 정확하게는 for ). 따라서 .BB::xBxA::xU = intU = decltype((void) A::x, 0)HasX<A>::value == true

6) 이름 해제 U:

음, (5)의 코드를 다시 보면 U이 선언 ( typename U) 이 아닌 곳에서 사용되는 것을 알 수 있습니다 . 그런 다음 두 번째 템플릿 인수의 이름을 해제하고 코드를 얻을 수 있습니다.


질문 의 연장으로 닫힌 질문 에서 여기로 리디렉션 됩니다. 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 { };

그게 다야. 그리고 그것이 어떻게 사용하는지 ( 실제 예 ) :

#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;
}

업데이트 : 최근에 원래 답변에 게시 한 코드로 더 많은 작업을 수행 할 수 있고 추가를 고려하여 업데이트하고 있습니다.

다음은 몇 가지 사용 스 니펫입니다. *이 모든 것에 대한 배짱은 훨씬 아래에 있습니다.

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;
};

매크로 (엘 디아블로!) :

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)

Boost.ConceptTraits 는 다음과 같은 형식의 유형 특성을 정의하는 것과 같이 유형 특성을 정의하는 일부 매크로를 다른 매크로간에 제공합니다 BOOST_TT_EXT_DEFINE_HAS_MEMBER(name).

has_member_##name<T>

T 에라는 멤버 유형이 있으면 참이됩니다. 그러나 이것은 참조 유형 멤버를 감지하지 않습니다.

당신의 경우 헤더 파일을 추가하는 것으로 충분할 것입니다

BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x)

다음과 같이 확인하십시오.

BOOST_STATIC_ASSERT(has_member_x<P1>::value);

사용 된 기술은 이전 답변 중 일부에서 설명한 것과 동일합니다.

불행히도이 라이브러리는 더 이상 유지되지 않습니다. 이제 C ++ 0x에 개념이 포함되지 않으므로 SFINAE와 함께이 라이브러리는 대부분의 개념과 함께 작동하는 완벽한 대체품입니다.


다음과 같은 전문화를 사용하지 않는 이유 :

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; }

이에 대한 두 번째 답변 (litb)은 구성원을 감지하는 방법을 보여줍니다.

함수의 존재를 확인하는 템플릿을 작성할 수 있습니까?


Check_x의 템플릿 전문화를 생성하지 않는 이유는 무엇입니까?

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

도대체 내가 생각하면. 두 가지 유형 만있는 경우이를 위해 템플릿이 필요한 이유는 무엇입니까?


추상 기본 클래스의 함수 (x, X, y, Y)가 있습니까, 아니면 그렇게되도록 리팩토링 할 수 있습니까? 그렇다면 Modern C ++ Design의 SUPERSUBCLASS () 매크로를이 질문에 대한 답변의 아이디어와 함께 사용할 수 있습니다.

컴파일 시간 유형 기반 디스패치


컴파일 타임에 얻을 수 있습니다 : 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() {}
};

참고 URL : https://stackoverflow.com/questions/1005476/how-to-detect-whether-there-is-a-specific-member-variable-in-class

반응형