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

StackOverflow https://stackoverflow.com/questions/1005476

문제

알고리즘 템플릿 함수를 만들려면 템플릿 인수 인 클래스에서 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
}

Usenet에있는 누군가의 훌륭한 아이디어를 기반으로합니다.

참고 : Hasx는 임의 유형으로 X라는 모든 데이터 또는 기능 멤버에 대해 확인합니다. 회원 이름을 소개하는 유일한 목적은 멤버 이름 조회에 대한 모호성을 갖는 것입니다. 회원 유형은 중요하지 않습니다.

다른 팁

다음은보다 간단한 솔루션입니다 Johannes 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 == true 그리고 HasX<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 == 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) 유형을 생성합니다 표현. 예를 들어, 0 유형이 있습니다 int 이와 같이, decltype(0) 수단 int. 유사하게, 1.2 유형이 있습니다 double 따라서, decltype(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 유형이 있습니다 bool 그리고 decltype(true, 0) 수단 int 또한.

클래스 유형은 어떻습니까? Instace의 경우, 무엇을합니다 decltype(f, 0) 평균? 이것이 여전히 의미를 기대하는 것은 당연합니다 int 그러나 그렇지 않을 수도 있습니다. 실제로 함수와 비슷한 쉼표 연산자에게 과부하가있을 수 있습니다. func 위의 a foo 그리고 int 그리고 반환 a char. 이 경우 decltype(foo, 0) ~이다 char.

쉼표 연산자에 과부하를 사용하는 것을 어떻게 피할 수 있습니까? 글쎄, 쉼표 연산자에게 void 피연산자와 우리는 무엇이든 캐스팅 할 수 있습니다 void. 그러므로, decltype((void) f, 0) 수단 int. 물론, (void) f 캐스트 f ~에서 foo 에게 void 기본적으로 표현이 유형이있는 것으로 간주되어야한다고 말하는 것 외에는 아무것도하지 않습니다. 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 { };

다시 여기 군, decltype((void) T::x, 0) 말하는 멋진 방법입니다 int 그러나 sfinae의 이익과 함께.

언제 T 유형으로 대체되면 잘못된 구조가 나타날 수 있습니다. 예를 들어, bool::x 유효한 C ++가 아니므로 대체합니다 T ~와 함께 bool 안에 T::x 유효하지 않은 구조를 산출합니다. sfinae 원칙에 따라 컴파일러는 코드를 거부하지 않고 단순히 그것을 무시합니다 (). 우리가 본 것처럼 더 정확하게HasX<bool> 실제로 의미합니다 HasX<bool, int>. 전문화 U = int 선택해야하지만 인스턴스화하는 동안 컴파일러는 찾습니다. bool::x 그리고 템플릿 전문화가 마치 존재하지 않는 것처럼 무시합니다.

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

사용 된 것과 동일한 주장 bool 보류 B ~부터 B::x 유효하지 않은 구조입니다 (B 회원이 없습니다 x). 하지만, A::x 괜찮아요. 컴파일러는 전문화를 인스턴스화하는 데 아무런 문제가 없습니다. U = int (또는 더 정확하게 U = decltype((void) A::x, 0)). 따라서, HasX<A>::value == true.

6) unnaming U:

글쎄, (5)의 코드를 다시보고, 우리는 그 이름이 U 어디서나 사용되지 않고 선언에서 사용됩니다 (typename U). 그런 다음 두 번째 템플릿 인수를 풀고이 게시물의 맨 위에 표시된 코드를 얻을 수 있습니다.

나는 여기서 a 의문 이것은 이것의 복제본으로 닫혔습니다. 나는 그것이 오래된 스레드라는 것을 알고 있지만 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 { };

그게 다야. 그리고 여기에 그것이 어떻게 사용되는지 (라이브 예):

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

매크로 (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)

boost.conceptraits 예를 들어 유형 특성을 정의하기 위해 다른 매크로를 제공합니다. 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와 함께이 라이브러리는 대부분의 개념과 함께 작동하기에 완벽한 대체입니다.

다음과 같은 전문화를 사용하지 않는 이유는 무엇입니까?

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)가 또는 그렇게되도록 리팩토링 될 수 있습니까? 그렇다면 현대 C ++ 디자인의 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() {}
};
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top