2013-03-15 1 views
24

나는 이미 SFINAE 관용구를 꽤 많이 사용했으며 리턴 유형이 아닌 템플릿 매개 변수에 내 std::enable_if<>을 사용했습니다. 그러나, 나는 그것이 작동하지 않는 약간의 사소한 경우를 보았고, 나는 왜 확실하지 않다. 우선, 여기 내 주요 :SFINAE는 반환 형식으로 작동하지만 템플릿 매개 변수로 사용하지 않습니다.

template<typename T, 
     typename = typename std::enable_if<std::is_integral<T>::value>::type> 
auto foo(T) 
    -> void 
{ 
    std::cout << "I'm an integer!\n"; 
} 

template<typename T, 
     typename = typename std::enable_if<std::is_floating_point<T>::value>::type> 
auto foo(T) 
    -> void 
{ 
    std::cout << "I'm a floating point number!\n"; 
} 

그리고 여기에 잘 작동 코드의 가정으로 해당 작품이다 :

다음
int main() 
{ 
    foo(5); 
    foo(3.4); 
} 

오류를 유발하는 foo의 구현입니다
template<typename T> 
auto foo(T) 
    -> typename std::enable_if<std::is_integral<T>::value>::type 
{ 
    std::cout << "I'm an integrer!\n"; 
} 

template<typename T> 
auto foo(T) 
    -> typename std::enable_if<std::is_floating_point<T>::value>::type 
{ 
    std::cout << "I'm a floating point number!\n"; 
} 

제 질문은 : foo의 첫 번째 구현이 두 번째 오류가 발생하지 않는 동안 오류를 발생시키는 이유는 무엇입니까?

main.cpp:14:6: error: redefinition of 'template<class T, class> void foo(T)' 
auto foo(T) 
    ^
main.cpp:6:6: note: 'template<class T, class> void foo(T)' previously declared here 
auto foo(T) 
    ^
main.cpp: In function 'int main()': 
main.cpp:23:12: error: no matching function for call to 'foo(double)' 
    foo(3.4); 
      ^
main.cpp:6:6: note: candidate: template<class T, class> void foo(T) 
auto foo(T) 
    ^
main.cpp:6:6: note: template argument deduction/substitution failed: 
main.cpp:5:10: error: no type named 'type' in 'struct std::enable_if<false, void>' 
      typename = typename std::enable_if<std::is_integral<T>::value>::type> 
     ^

편집 :

Working codefaulty code.

+1

좋아. 실제 데모 : [1 차 컴파일에 실패했습니다] (http://ideone.com/mJ8Zp6) 및 [2 번째 성공적으로 컴파일 된 부분] (http://ideone.com/G0jBft). –

+0

추가 정보 : VS 2012 년 11 월 CTP와 동일. –

+3

[This] (http://flamingdangerzone.com/cxx11/2012/06/01/almost-static-if.html)은 당신에게 완벽한 읽을 거리 여야합니다. – Xeo

답변

26

기능 템플릿 동등성이 정의 된 14.5.6.1 Function template overloading (C++ 11 표준)을 살펴보십시오. 즉, 기본 템플릿 인수는 고려되지 않으므로 첫 번째 경우에는 동일한 함수 템플릿이 두 번 정의됩니다. 두 번째 경우에는 반환 형식 (14.5.6.1/4 참조)에 사용 된 템플릿 매개 변수를 나타내는 표현식이 있습니다. 이 표현식은 서명의 일부이기 때문에 두 개의 다른 함수 템플릿 선언을 얻게되므로 SFINAE는 작업 할 수있는 기회를 갖게됩니다.

+0

고맙습니다. 이 설명은 최소한 간단하고 명확합니다. 나는이 규칙에 대해 잘 모른다. :) – Morwenn

7

템플릿의 = ...은 기본 매개 변수를 제공합니다. 이 에 대한

template<typename T, typename> auto foo(T a); 

같은 두
기능을 보이는 실제 서명의 일부가 아닙니다.

필요에 따라이 문제의 가장 일반적인 해결책은 태그 발송을 사용하는 것입니다. 템플릿에서

struct integral_tag { typedef integral_tag category; }; 
struct floating_tag { typedef floating_tag category; }; 

template <typename T> struct foo_tag 
: std::conditional<std::is_integral<T>::value, integral_tag, 
        typename std::conditional<std::is_floating_point<T>::value, floating_tag, 
               std::false_type>::type>::type {}; 

template<typename T> 
T foo_impl(T a, integral_tag) { return a; } 

template<typename T> 
T foo_impl(T a, floating_tag) { return a; } 

template <typename T> 
T foo(T a) 
{ 
    static_assert(!std::is_base_of<std::false_type, foo_tag<T> >::value, 
       "T must be either floating point or integral"); 
    return foo_impl(a, typename foo_tag<T>::category{}); 
} 

struct bigint {}; 
template<> struct foo_tag<bigint> : integral_tag {}; 

int main() 
{ 
    //foo("x"); // produces a nice error message 
    foo(1); 
    foo(1.5); 
    foo(bigint{}); 
} 
+2

이것은 매우 일반적인 것이 아니다 -이 정확한 경우에만 해당 - 정수 대 부동. – einpoklum

2

값이 작동 :

template<typename T, 
     typename std::enable_if<std::is_integral<T>::value, int>::type = 0> 
auto foo(T) 
    -> void 
{ 
    std::cout << "I'm an integer!\n"; 
} 

template<typename T, 
     typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0> 
auto foo(T) 
    -> void 
{ 
    std::cout << "I'm a floating point number!\n"; 
}