2014-06-13 2 views
3

C 언어의 Member Detector이라는 관용구를 보았습니다.이 특성은 클래스에 특정 이름의 멤버가 있는지 알려주는 유형 특성입니다. 그러나 형식이 클래스가 아닌 경우 기대했던대로 링크 된 예제가 작동하지 않습니다. 모든 클래스가 아닌 형식에 대해 false 결과를 원했습니다. 가능한 해결책은 확실히 boost::is_class<T>를 사용하여이 같은입니다 :SFINAE 유형의 인스턴스화

template<typename T> 
struct general_DetectX : boost::mpl::and_< 
    boost::is_class<T>, 
    DetectX<T> >::type 
{ }; 

bool hasX = general_DetectX<int>::value; // hasX = false 

그러나이 질문은 원래 DetectX<T> 대신 SFINAE 일을하는 오류를 발생 이유에 관한 것입니다. 여기에 연결된 코드의 관련 부분의 발췌가 (간략화를 위해 제거 로컬 구조체 FallbackCheck<U,U> 및 형식 정의 ArrayOfOne, ArrayOfTwotype)

template<typename T> 
class DetectX 
{ 
     struct Derived : T, Fallback { }; 

     template<typename U> 
     static ArrayOfOne & func(Check<int Fallback::*, &U::X> *); 

     template<typename U> 
     static ArrayOfTwo & func(...); 

    public: 
     enum { value = sizeof(func<Derived>(0)) == 2 }; 
}; 
그것은 DetectX::Derived 것을 알 수

모든 오버로드 해결 밖에 사용 , 오류 처리를위한 SFINAE 규칙이 호출되지 않습니다. 그러나 이것은 Derived의 사용가 오버로드 확인의 일환으로 일어날 않는 경우에 변경 될 수 있습니다

template<typename T> 
class DetectX 
{ 
    template<typename U> 
    struct Derived : U, Fallback { }; 

    template<typename U> 
    static ArrayOfOne & func(Check<int Fallback::*, &Derived<U>::X> *); 

    template<typename U> 
    static ArrayOfTwo & func(...); 

public: 
    enum { value = sizeof(func<T>(0)) == 2 }; 
}; 

Derived<T> 템플릿을 처음 func() 과부하를 인스턴스화 할 때에만 인스턴스화, 왜 난 여전히 오류를받을 수 있나요 수정 된 버전의 경우에도?

$ g++ --version | head -n1 
g++ (GCC) 4.8.2 
$ g++ -c demo.cxx 
demo.cxx: In instantiation of 'struct DetectX<int>::Derived<int>': 
demo.cxx:16:53: required by substitution of 'template<class U> static char (& DetectX<T>::func(DetectX<T>::Check<int DetectX<T>::Fallback::*, (& DetectX<T>::Derived<U>::X)>*))[1] [with U = U; T = int] [with U = int]' 
demo.cxx:24:31: required from 'class DetectX<int>' 
demo.cxx:27:25: required from here 
demo.cxx:7:12: error: base type 'int' fails to be a struct or class type 
    struct Derived : U, Fallback { }; 
      ^
+0

기본 클래스 목록에서 대체가 발생하기 때문에 오류가 발생합니다. * 모든 오류로 인해 소프트 오류 (SFINAE)가 아닌 오류가 발생합니다. 즉, 기본 클래스에서는 SFINAE가 발생하지 않습니다. – Nawaz

+0

@Nawaz 하드 및 소프트 오류를 ​​소개하는 의견을 보내 주셔서 감사합니다. 그러나 Jarod42가 제공 한 http://stackoverflow.com/questions/15260685에 대한 링크가 필요하여 문맥을 얻으 려했습니다. –

답변

2

당신은 사용할 수 있습니다 : 예를 들면 다음과 같습니다이다 (https://ideone.com/LArNVO)

#include <cstdint> 
#include <type_traits> 

#define DEFINE_HAS_MEMBER(traitsName, memberName)        \ 
    template <typename U>              \ 
    class traitsName               \ 
    {                   \ 
    private:                 \ 
     struct Fallback { int memberName; };         \ 
     struct Dummy {};              \ 
     template<typename T, bool is_a_class = std::is_class<T>::value>  \ 
     struct identity_for_class_or_dummy { using type = Dummy; };   \ 
     template<typename T>             \ 
     struct identity_for_class_or_dummy<T, true> { using type = T; };  \ 
                       \ 
     template <typename Base>            \ 
     struct Derived : Base, Fallback {};         \ 
     template<typename T, T> struct helper;        \ 
     template<typename T>             \ 
     static std::uint8_t             \ 
     check(helper<int (Fallback::*),          \ 
       &Derived<typename identity_for_class_or_dummy<T>::type>::memberName>*); \ 
     template<typename T> static std::uint16_t check(...);     \ 
    public:                 \ 
     static                \ 
     constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint16_t); \ 
    } 

DEFINE_HAS_MEMBER(has_foo, foo); 

// Now test it: 
class C{ public: int foo; }; 
class D : public C {}; 
class E {}; 

static_assert(has_foo<C>::value, ""); 
static_assert(has_foo<D>::value, ""); 
static_assert(!has_foo<E>::value, ""); 
static_assert(!has_foo<int>::value, ""); 

이 짧은 SFINAE에서 질문 What is exactly the “immediate context” mentioned in the C++11 Standard for which SFINAE applies?

다음의 사건에 실패하는 이유 당신은 Derived<U>::X 적용 이해할 수 있지만 Derived<U> (귀하의 경우에는 불량 함)이 아닙니다.

+0

DEFINE_HAS_MEMBER 예제를 가져 주셔서 감사합니다. 그러나이 응용 프로그램에서는 사용할 수 없습니다 : 1. C++ 11을 사용하지만 C++ 03을 사용하는 것을 잊어 버렸습니다 (minor : 재 작성 가능). 2. 다른 솔루션에는 필요하지 않은 구성원의 서명이 필요합니다. 그러나 http://stackoverflow.com/questions/15260685에 대한 링크는 정말로 도움이되었습니다. 기본적으로'Derived '의 인스턴스 생성은 SFINAE가 적용되지 않는'func '인스턴스 생성의 첫 번째 단계에서 발생합니다. –

+0

* 2. *의 경우 구현을 수정했습니다. ('T :: foo'에 대한 서명이 체크되지 않기 때문에). – Jarod42

+0

덕분에 나는이 경우에도 당신의 기술을 사용했습니다. –