2013-03-15 109 views
24

我已經使用了SFINAE成語很多次,我習慣於把我的std::enable_if<>放在模板參數中而不是返回類型中。但是,我遇到了一些無效的小事,我不知道爲什麼。首先,這裏是我的主:SFINAE工作返回類型,但不作爲模板參數

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

這裏是foo實現觸發錯誤:

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

這裏是一個假想相當於一段代碼,正常工作:

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

確定。實際演示:[第一次未能編譯部分](http://ideone.com/mJ8Zp6)和[第二次成功編譯部分](http://ideone.com/G0jBft)。 – 2013-03-15 08:34:20

+0

附加信息:與2012年11月份的CTP相同。 – 2013-03-15 08:45:44

+3

[This](http://flamingdangerzone.com/cxx11/2012/06/01/almost-static-if.html)應該是您的完美閱讀。 – Xeo 2013-03-15 09:04:30

回答

26

您應該看看14.5.6.1 Function template overloading(C++ 11標準),其中定義了功能模板等同性。簡而言之,不考慮默認的模板參數,所以在第一種情況下,您有兩次定義相同的函數模板。在第二種情況下,您有表達式引用返回類型中使用的模板參數(請參閱14.5.6.1/4)。由於該表達式是簽名的一部分,因此您可以獲得兩個不同的函數模板聲明,因此SFINAE有機會工作。

+0

非常感謝。至少這個解釋是簡單明瞭的。我不知道這個規則:) – Morwenn 2013-03-15 08:55:51

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 2015-11-20 01:13:33

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"; 
} 
相關問題