2012-04-26 2 views
5

SFINAE에 문제가 있습니다. 유형에 멤버 함수가 있는지 여부를 결정할 수 있어야합니다. operator => 반환 유형에 관계없이 정의됩니다. 예제는 다음과 같습니다.SFINAE - 템플릿 형식에 '변수'반환 형식의 멤버 함수가 있는지 확인하려고 시도했습니다.

이 클래스는 테스터에 있습니다. X *의 리턴 유형으로 연산자 ->()를 정의합니다. 따라서 나는 어디에서나 'X'가 하드 코드하는 것을 모른다.

template <class X> 
class PointerX 
{ 
    ... 

    X* operator->() const; 
    ... 
} 

이 클래스는 T에서 전달 된 연산자 operator-> defined가 있는지 확인하려고 시도합니다. 에 관계없이 연산자 -> 반환 유형입니다.

template<typename T> 
struct HasOperatorMemberAccessor 
{ 
    template <typename R, typename C> static R GetReturnType(R (C::*)()const); 

    template<typename U, typename R, R(U::*)()const> struct SFINAE{}; 
    template<typename U> static char Test(SFINAE<U,  decltype(GetReturnType(&U::operator->)), &U::operator-> >*); 
    template<typename U> static uint Test(...); 
    static const bool value = sizeof(Test<T>(0)) == sizeof(char); 
}; 

이 클래스는 operator-> return 유형이 'Object'여야한다는 점을 제외하고는 완전히 똑같습니다.

template<typename T> 
struct HasOperatorMemberAccessorOBJECT 
{ 
    template <typename R, typename C> static R GetReturnType(R (C::*)()const); 

    template<typename U, typename R, R(U::*)()const> struct SFINAE{}; 
    template<typename U> static char Test(SFINAE<U,  Object*,    &U::operator-> >*); // only change is we hardcoded Object as return type. 
    template<typename U> static uint Test(...); 
    static const bool value = sizeof(Test<T>(0)) == sizeof(char); 
}; 

결과 :

void main() 
{ 
    HasOperatorMemberAccessor<PointerX<Object>>::Test<PointerX<Object>>(0);   // fails ::value is false; Test => Test(...) 

    HasOperatorMemberAccessorOBJECT<PointerX<Object>>::Test<PointerX<Object>>(0);  // works! ::value is true; Test => Test(SFINAE<>*) 
} 

HasOperatorMemberAccessor는 PointX의 멤버 함수 "->() const를 개체 연산자"를 찾을 수 없습니다. 그래서 Test의 일반 버전 Test (...)를 사용합니다.

그러나 HasOperatorMemberAccessorOBJECT는 PointX의 "Object operator -> (const)"를 찾을 수있었습니다. 따라서 Test Special Version Test (SFINAE *)를 사용합니다.

둘 다 "Object operator -> (const)"메서드를 찾을 수 있어야합니다. 따라서 둘 다 Test의 Special version Test (SFINAE *)를 사용해야합니다. 따라서 HasOperatorMemberAccessor> :: value는 둘 다 true 여야합니다. HasOperatorMemberAccessor과 HasOperatorMemberAccessorOBJECT 사이의 유일한 차이는 HasOperatorMemberAccessorOBJECT 객체에 하드 코딩 유형 이름의 R을 가지고 있다는 것입니다

,

그래서이 문제는 "decltype (GetReturnType (& U :: 운용자>))"반환하지 않습니다 객체입니다 바르게. 리턴 타입을 발견하기위한 여러 가지 허가를 시도했다. 그들은 다음과 같이 구성됩니다 :

decltype(GetReturnType(&U::operator->)) 
    typename decltype(GetReturnType(&U::operator->)) 
    decltype(((U*)nullptr)->operator->()) 
    typename decltype(((U*)nullptr)->operator->()) 

아무 이유없이 작동합니까? MSVC++ 10.0을 사용하고 있습니다.

+0

눈에 띄는 것은 'PointerX :: operator->'는'bool *'이 아니라'bool *'을 반환한다는 것입니다. –

+0

PointerX의 X 유형은 HasOperatorMemberAccessor와 관련이 없습니다. 예제에 많은 외부 객체를 추가하지 않음으로써 내 문제를 일반화하려고했습니다. 너무 혼란 스럽다면, 나는 bool을 문자열로 변경할 것입니다. –

+0

다시 시도해주세요.'PointerX :: operator->'는'Object *'를 반환합니다. 'decltype (...)'은'Object *'가 될 것입니다. 'HasOperatorMemberAccessorOBJECT'는'decltype (...)'을'Object'로 대체하기는하지만 외관상 성공한 것 같습니다. 여기 뭔가 잘못 된 것 같습니다. 나는'HasOperatorMemberAccessor'에 대해서 전혀 이야기하지 않는다. –

답변

4

이러한 특성을 구현하는 방법을 묻는 것이거나 decltype이 예상대로 작동하지 않는 이유는 무엇입니까?

#include <type_traits> 

template<typename T, bool DisableB = std::is_fundamental<T>::value> 
struct HasOperatorMemberAccessor 
{ 
private: 
    typedef char no; 
    struct yes { no m[2]; }; 

    struct ambiguator { char* operator ->() { return nullptr; } }; 
    struct combined : T, ambiguator { }; 
    static combined* make(); 

    template<typename U, U> struct check_impl; 
    template<typename U> 
    static no check(
     U*, 
     check_impl<char* (ambiguator::*)(), &U::operator ->>* = nullptr 
    ); 
    static yes check(...); 

public: 
    static bool const value=std::is_same<decltype(check(make())), yes>::value; 
}; 

// false for fundamental types, else the definition of combined will fail 
template<typename T> 
struct HasOperatorMemberAccessor<T, true> : std::false_type { }; 

// true for non-void pointers 
template<typename T> 
struct HasOperatorMemberAccessor<T*, false> : 
    std::integral_constant< 
     bool, 
     !std::is_same<typename std::remove_cv<T>::type, void>::value 
    > 
{ }; 

template<typename X> 
struct PointerX 
{ 
    X* operator ->() const { return nullptr; } 
}; 

struct X { }; 

int main() 
{ 
    static_assert(
     HasOperatorMemberAccessor<PointerX<bool>>::value, 
     "PointerX<> has operator->" 
    ); 
    static_assert(
     !HasOperatorMemberAccessor<X>::value, 
     "X has no operator->" 
    ); 
    static_assert(
     HasOperatorMemberAccessor<int*>::value, 
     "int* is dereferencable" 
    ); 
    static_assert(
     !HasOperatorMemberAccessor<int>::value, 
     "int is not dereferencable" 
    ); 
    static_assert(
     !HasOperatorMemberAccessor<void*>::value, 
     "void* is not dereferencable" 
    ); 
} 

VC++ 2010

이 훨씬 청소기 만드는 데 필요한 필요한 C++ (11) 시설 (예를 들어 표현 SFINAE를) 부족 : 전자의 경우, 여기에 한 가지 방법입니다.

+0

글쎄, 이건 T가 포인터 타입이 될 수 없다는 점을 제외하면 나에게 꽤 효과적이었다.베이스를 다음으로 변경했습니다 : struct base : 조건부 :: value, T, no> :: type, base_mixin {}; 그렇다면 왜 decltype이 작동하지 않는지 알고 싶습니다. –

+0

@MichaelG :'HasOperatorMemberAccessor <>'는 포인터에 대해 매우 전문화 될 수 있습니다 : template class HasOperatorMemberAccessor : public std :: true_type {};'. 'decltype'이 예상대로 작동하지 않는다면 코드를 많이 조사하지는 않았지만 VC++ 2010의'decltype' 구현은 사전 표준이며 버그가 있습니다. – ildjarn

+0

@Michael : 적절한 포인터 유형 감지로 편집되었습니다 ('void *'는 잡았다). – ildjarn

관련 문제