2013-03-15 28 views
24

Tôi đã sử dụng thành ngữ SFINAE một vài lần và tôi đã sử dụng để đặt các thông số mẫu của mình thay vì trả về. Tuy nhiên, tôi đã xem xét một số trường hợp tầm thường mà nó không hoạt động, và tôi không chắc tại sao. Trước hết, đây là của tôi chính:SFINAE hoạt động theo kiểu trả về chứ không phải là thông số mẫu

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

Đây là một thực hiện foo đã kích hoạt lỗi:

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

Và đây là một mảnh được cho là tương đương với mã mà hoạt động tốt:

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

Câu hỏi của tôi là: tại sao việc triển khai đầu tiên của foo gây ra lỗi trong khi lỗi thứ hai không kích hoạt?

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> 
     ^

EDIT:

Working codefaulty code.

+1

Ok. Trình diễn thực tế: [1 không biên dịch được] (http://ideone.com/mJ8Zp6) và [phần biên dịch thành công thứ 2] (http://ideone.com/G0jBft). –

+0

Thông tin bổ sung: cùng với VS 2012 tháng 11 CTP. –

+3

[Điều này] (http://flamingdangerzone.com/cxx11/2012/06/01/almost-static-if.html) nên được đọc hoàn hảo cho bạn. – Xeo

Trả lời

26

Bạn nên xem 14.5.6.1 Function template overloading (tiêu chuẩn C++ 11) trong đó các mẫu chức năng tương đương được xác định. Tóm lại, các đối số mẫu mặc định không được xem xét, vì vậy trong trường hợp đầu tiên bạn có cùng một mẫu chức năng được xác định hai lần. Trong trường hợp thứ 2, bạn có tham số mẫu tham chiếu biểu thức được sử dụng trong kiểu trả về (xem lại 14.5.6.1/4). Vì biểu thức này là một phần của chữ ký, bạn nhận được hai khai báo mẫu hàm khác nhau và do đó SFINAE có cơ hội làm việc.

+0

Cảm ơn bạn rất nhiều. Lời giải thích này đơn giản và rõ ràng ít nhất. Tôi không có ý tưởng về quy tắc này :) – Morwenn

7

= ... của mẫu chỉ cung cấp thông số mặc định. Đây không phải là một phần của chữ ký thực tế trông giống như

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

cho cả chức năng.

Tùy thuộc vào nhu cầu của bạn, giải pháp chung nhất cho vấn đề này là sử dụng gửi thẻ.

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

Đây không phải là rất chung chung - đó là cụ thể cho trường hợp này chính xác - số nguyên vs nổi. – einpoklum

2

giá trị trong các mẫu làm việc:

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"; 
} 
Các vấn đề liên quan