2013-06-15 34 views
10

Tôi đã hỏi một câu hỏi khoảng một tuần trước để hỏi làm thế nào tôi có thể đơn giản khởi tạo một mẫu lớp chỉ khi kiểu nó đã có một hàm thành viên cụ thể. Trong câu trả lời của tôi, tôi đã nhận được một giải pháp phức tạp. Nhưng sau đó tôi đã cố gắng làm điều đó một mình. Tôi chỉ muốn biết nếu điều này đủ để tìm ra một loại nhất định T có hàm void có tên là f lấy 0 tham số.Đây có phải là SFINAE không?

#include <type_traits> 
#include <utility> 

template <typename T, typename = void> 
struct has_f : std::false_type { }; 

template <typename T> 
struct has_f< 
    T, 
    decltype(std::declval<T>().f(), void())> : std::true_type { }; 

template <typename T, typename = typename std::enable_if<has_f<T>::value>::type> 
struct A { }; 

struct B 
{ 
    void f(); 
}; 

struct C { }; 

template class A<B>; // compiles 
template class A<C>; // error: no type named ‘type’ 
        // in ‘struct std::enable_if<false, void>’ 

Nếu vậy, tại sao là những câu trả lời khác rất phức tạp trong this thread?

+2

Có, đây là SFINAE và có, điều này là đúng. Đối với lý do tại sao các câu trả lời khác phức tạp hơn ... khó nói. –

+0

Để làm rõ 'has_f' tự dựa vào SFINAE,' A' thì không. –

+0

Như một khả năng, SFINAE hỗ trợ trong một số trình biên dịch (* ho * MSVC2012) loại hút, vì vậy một số phức tạp đó có thể là giải pháp để có một SFINAE di động hơn. Là một sang một bên, 'std :: declval () .f()' và 'std :: declval () .f()' (hoặc là nó 'std :: declval () .f()'?) có thể là những thứ khác nhau trong C++ 1y do tham chiếu rvalue đối với tính năng 'this' này. – Yakk

Trả lời

7

Yep, bạn đã giải quyết nó trong đơn giản, phong cách thành ngữ nhất của C++ 11 SFINAE.

Lưu ý rằng bạn không kiểm tra xem kiểu trả về là void, là một thành viên không phải là thành viên không có nghĩa là không có tham số. f chỉ đơn giản là có thể gọi được mà không có đối số. Nó thậm chí có thể là một functor.

Để kiểm tra một hàm thành viên không tĩnh nullary trở void, sử dụng

template <typename T> 
struct has_f<T, decltype(void(static_cast< void (T::*)(void) >(&T::f))) > 
    : std::true_type {}; 

template <typename T> 
struct has_f<T, decltype(void(static_cast< void (T::*)(void) const >(&T::f))) > 
    : std::true_type {}; 
+0

Vì vậy, tôi sử dụng 'std :: result_of' để kiểm tra xem kiểu trả về có bị vô hiệu không? –

+0

@MemyselfandI Đó là một cách, bạn cũng có thể sử dụng 'decltype'. 'std :: result_of' thực hiện một điệu nhảy lạ để hỗ trợ khái niệm INVOKE được sử dụng bởi' std :: bind' và 'std :: function'. Xem chỉnh sửa của tôi để biết cách nhanh chóng kiểm tra một số điều kiện cùng một lúc. Trong mọi trường hợp, bạn cũng sẽ cần 'std :: is_same'. – Potatoswatter

+0

Tôi nghĩ rằng bạn có nghĩa là 'std :: is_same :: giá trị' (di chuyển đóng ngoặc), và tương tự cho dòng dưới đây. – hvd