2015-01-20 1 views
0

SFINAE가 "has_member"유형의 구조체에 적용되어 놀았습니다 (here).is_enum이 잘못된 동작을 일으키는 경우?

그래서이 솔루션을 더 간단하게 만들기 위해 C++ 11의 일부 기능을 사용하려고했습니다. 열거 형의 존재 여부를 확인하는 데 몇 가지 문제가 있지만 여기서 논리 오류를 찾을 수 없습니다. 이 사용 GCC 4.9.2을 실행

#include<iostream> 

template<typename T > 
struct has_enum_name 
{ 
private: 
    // ***** this is the line that isn't working ***** 
    // If U has an enum "name", then I expect is_enum to return true… 
    // Then I expect enable_if<>::type to be std::true_type…etc. This isn't what happens. 
    template<typename U> static auto test(int) -> decltype(
    std::enable_if<std::is_enum< typename U::name >::value, std::true_type>::type()); 

    template<typename U> static auto test(...) -> std::false_type; 
public: 
    static constexpr bool value = 
    !(std::is_same<decltype(test<T>(0)),std::false_type>::value); 

    static constexpr bool is_enum() 
    { 
    return std::is_enum<typename T::name>::value; 
    } 
}; 

template<typename T > 
struct has_enum_name_2 
{ 
private: 
    template<typename U> static auto test(int) -> decltype( 
    std::enable_if<true, std::true_type>::type()); 

    template<typename U> static auto test(...) -> std::false_type; 
public: 
    static constexpr bool value = 
    !(std::is_same<decltype(test<T>(0)),std::false_type>::value); 
}; 

struct Foo 
{ 
    enum class name 
    { 
    enum1, 
    enum2 
    }; 
}; 

int main() 
{ 
    std::cout<<"std::is_enum<Foo::name>::value = "<<std::is_enum<Foo::name>::value<<std::endl; 
    std::cout<<"has_enum_name<Foo>::value  = "<<has_enum_name<Foo>::value<<std::endl; 
    std::cout<<"has_enum_name<Foo>::is_enum() = "<<has_enum_name<Foo>::is_enum()<<std::endl; 
    std::cout<<"has_enum_name_2<Foo>::value = "<<has_enum_name_2<Foo>::value<<std::endl; 
} 

첫 번째 출력 라인은 표준 : is_enum는 푸 :: 이름을 열거 올바르게 작동하는지 확인

$ ./a.out 
std::is_enum<Foo::name>::value = 1 
has_enum_name<Foo>::value  = 0 
has_enum_name<Foo>::is_enum() = 1 
has_enum_name_2<Foo>::value = 1 

제공 : 코드입니다.

두 번째 줄은 struct constexpr "has_enum_name :: value"의 결과를 출력합니다. 난 그냥 std :: enable_if 함께 std :: is_enum decltype 반환 할 수 있도록 사용하려고 ...이 경우 std :: true_type(). 보시다시피, 출력은 false를 반환합니다 ... 그래서 줄 :

template<typename U> static auto test(int) -> decltype( 
    std::enable_if<std::is_enum< typename U::name >::value, std::true_type>::type()); 

내가 생각하는 것처럼 작동하지 않습니다. 나는 is_enum이 true를 반환해야한다고 생각하는데, 이는 enable_if가 true_type을 반환해야하고 decltype이 true_type()을 반환해야 함을 의미합니다.

다음 출력은 std :: is_enum <이 구조체에서 올바르게 작동하는지 확인하는 것입니다. 그렇습니다.

다음 출력은 "has_enum_name"구조체의 복사본이지만 의심되는 줄의 is_enum <을 하드 코딩 된 "true"로 바꿨습니다. 올바르게 작동합니다.

그래서 어떤 이유로 is_enum이 true를 반환하지 않는 것으로 보입니다. 어떤 아이디어가 내가 여기서 잘못하고있는 것인가?

답변

2

이와 같은 문제가 발생하면 컴파일러에서 도움을 받으십시오. 컴파일 오류를 강제로 (...) 과부하를 제거하고 GCC가 당신을 알려줍니다 무엇을 참조하십시오

test.cc: In instantiation of ‘constexpr const bool has_enum_name<Foo>::value’: 
test.cc:51:71: required from here 
test.cc:18:33: error: no matching function for call to ‘has_enum_name<Foo>::test(int)’ 
    !(std::is_same<decltype(test<T>(0)),std::false_type>::value); 
           ^
test.cc:18:33: note: candidate is: 
test.cc:12:36: note: template<class U> static decltype (std::enable_if<std::is_enum<typename U::name>::value, std::integral_constant<bool, true> >::type()) has_enum_name<T>::test(int) [with U = U; T = Foo] 
    template<typename U> static auto test(int) -> decltype(
            ^
test.cc:12:36: note: template argument deduction/substitution failed: 
test.cc: In substitution of ‘template<class U> static decltype (std::enable_if<std::is_enum<typename U::name>::value, std::integral_constant<bool, true> >::type()) has_enum_name<T>::test(int) [with U = Foo]’: 
test.cc:18:33: required from ‘constexpr const bool has_enum_name<Foo>::value’ 
test.cc:51:71: required from here 
test.cc:13:83: error: dependent-name ‘std::enable_if<std::is_enum<typename T::name>::value, std::integral_constant<bool, true> >::type’ is parsed as a non-type, but instantiation yields a type 
    std::enable_if<std::is_enum< typename U::name >::value, std::true_type>::type()); 
                       ^
test.cc:13:83: note: say ‘typename std::enable_if<std::is_enum<typename T::name>::value, std::integral_constant<bool, true> >::type’ if a type is meant 

을 잘못 정확히 무엇을 그 : 당신은 단지 typename를 추가하는 것을 잊었다. 은 type이 유형이 아닌 경우 유효 할 수 있습니다. 단지 함수 호출 일 수 있습니다. 이것이 컴파일러가 파싱하는 방법입니다.

한편, decltype(typename T::type())은 유형이 기본 구성 가능 여부를 확인하기 위해 특별히 시도하지 않는 한 다소 의미가없는 것처럼 보입니다.

template<typename U> static auto test(int) -> 
    typename std::enable_if<std::is_enum<typename U::name>::value, std::true_type>::type; 

을하고 typename없이, 당신은했습니다 것을 시도했다 경우 즉시 유용한 오류 메시지를 받았습니다 : 당신은 단순히과 같이 직접 typename T::type를 사용할 수 있습니다. 그 모두가 당신이 사용할 수있는 value 회원이 : 당신은 이미 당신이 false_type 또는 true_type 상대하고 알고 있기 때문에

value 귀하의 초기화도 불필요하게 복잡하다.

static constexpr bool value = decltype(test<T>(0))::value; 
+0

안녕하세요. (...)을 (를) 삭제 해 주셔서 감사합니다. 진짜 질문은 "SFINAE를 어떻게 디버깅해야합니까?"라는 질문이 있어야합니다.내가 아직도 혼란스러워하는 이유는 "std :: enable_if :: type"이 typename을 필요로하지 않고 "std :: enable_if :: value, std :: true_type> :: type; " 않습니다. – doc07b5

+1

@ doc07b5'std :: enable_if :: type'은 템플릿 매개 변수에 종속되지 않으므로 컴파일러는 템플릿을 파싱 할 때 유형임을 알 수 있습니다. 'std :: enable_if :: value, std :: true_type> :: type'은 템플릿 매개 변수'U'에 의존하기 때문에 컴파일러에게 예상하고 있다고 말할 필요가 있습니다 그것은 항상'typename' 키워드를 가진 타입을 산출합니다. – Casey

관련 문제