2014-10-14 21 views
6

Tại thời điểm này, tôi using this method to check if a class has a method with a specific signature.Sử dụng `void_t` để kiểm tra xem một lớp học có một phương pháp với một chữ ký cụ thể

Sau khi tham dự Walter E. Brown's metaprogramming CppCon2014 talk, tôi bắt đầu tự hỏi nếu void_t thể được sử dụng trong tình huống đặc biệt này để làm cho mã sạch hơn và dễ đọc hơn.

Tuy nhiên, tôi đang gặp sự cố khi suy nghĩ về số void_t - cho đến nay tôi hiểu rằng void_t có thể giúp tôi xác định thời gian biên dịch cho dù biểu thức có hợp lệ hay không.

Ví dụ:

template< class, class = void > 
struct has_type_data_member : false_type { }; 

template< class T > 
struct has_type_data_member<T, void_t<decltype(T::data)>> : true_type { }; 

Nếu decltype(T::type) là một biểu thức hợp lệ, has_type_data_member<T> sẽ là một sự thật thời gian biên dịch không đổi. Do đó, chúng tôi chắc chắn rằng T có trường thành viên được gọi là data.

Tôi muốn sử dụng phương pháp tương tự để kiểm tra xem loại T có phương pháp có tên cụ thể và chữ ký cụ thể hay không.

Giả sử tôi muốn kiểm tra xem T có phương thức được gọi là getCount() trả về int hay không. Đây là những gì tôi mong đợi để làm việc ((Ideone.com link)):

template< class, class = void > 
struct hasGetCount : false_type { }; 

template< class T > 
struct hasGetCount<T, VoidT<decltype(T::getCount)>> 
: std::is_same<decltype(std::declval<T>().getCount()), int>::type { }; 

Thật không may, các kiểm tra static_assert không vượt qua.

Tôi đang làm gì sai? Có thể sử dụng void_t trong trường hợp này không?

câu hỏi Bonus:

  • Làm thế nào có thể tôi cũng kiểm tra xem chữ ký phương pháp là tương đương với một chữ ký người sử dụng đi như trong việc thực hiện ban đầu?
  • tôi có thể sử dụng macro để xác định các loại cấu trúc helper như thế này:

    DEFINE_METHOD_CHECKER(hasGetCount, getCount); 
    // ... 
    static_assert(hasGetCount<ClassWithGetCount>::value == true, ""); 
    

    Có thể tránh được việc phải xác định một sau đó kiểm tra giá trị struct của struct đầu tiên? Tôi có nghĩa là, nó có thể sử dụng một vĩ mô để viết một cái gì đó như thế này? Ví dụ:

    static_assert(CHECK_METHOD(ClassWithGetCount, getCount)::value == true, ""); 
    
+1

A * id-expression * đặt tên cho một hàm thành viên không tĩnh không thể được sử dụng trong một 'decltype'. –

+0

@ T.C .: Tôi thấy - có cách nào khác để tạo ra một biểu thức hợp lệ/không hợp lệ tùy thuộc vào việc một kiểu có/không có một phương thức không? –

Trả lời

6

Thứ nhất, một id-biểu đặt tên một hàm thành viên không tĩnh không thể được sử dụng như một toán hạng unevaluated (chẳng hạn như các toán hạng của decltype). Hơn nữa, bạn nên kiểm tra cho dù toàn bộ biểu hiện chức năng cuộc gọi được cũng được hình thành, không chỉ là cho dù có là thành viên gọi là getCount:

template< class, class = void > 
struct hasGetCount : false_type { }; 

template< class T > 
struct hasGetCount<T, VoidT<decltype(std::declval<T>().getCount())>> 
: std::is_same<decltype(std::declval<T>().getCount()), int>::type { }; 

(Sử dụng declval<T&> nếu bạn muốn kiểm tra xem getCount() có thể được gọi vào một giá trị trái.)

Nếu bạn chỉ kiểm tra sự tồn tại của thành viên getCount, bạn sẽ gặp phải lỗi nghiêm trọng nếu thành viên có tên đó tồn tại nhưng không thể gọi (ví dụ: thành viên dữ liệu).

Mặc dù vào thời điểm này bạn cũng có thể xem xét chỉ sử dụng một cái gì đó giống như

template< class T > 
struct hasGetCount<T, std::enable_if_t<std::is_same<decltype(std::declval<T>().getCount()), int>::value>> : std::true_type { }; 

thay vì viết các decltype hai lần.

+2

'declval ' tạo ra một xvalue, vì vậy nếu bạn muốn kiểm tra nếu 'getCount()' có thể được gọi trên một giá trị lvalue, sử dụng 'declval ' (có liên quan kể từ khi vòng loại ref). – dyp

+0

SFINAE là kết quả của các quy tắc về chuyên môn mẫu lớp và độ phân giải quá tải liên quan đến các mẫu chức năng. 'enable_if' không phải là thành phần cần thiết cũng không đủ cho nó. Nếu bạn muốn viết lại bài kiểm tra ở dạng mẫu hàm, ví dụ như 'template typename std :: is_same () .getCount())> :: type test (int);' là một khởi đầu tốt và không sử dụng 'enable_if'. –

6

Bạn có thể sử dụng void_t để dễ dàng xác minh rằng các kiểu trả về của getCount là mui trần để int:

template< class, class = void > 
struct hasGetCount : false_type { }; 

template< class T > 
struct hasGetCount<T, 
    VoidT< 
    decltype(std::declval<int&>() = std::declval<T>().getCount()) 
    >> : std::true_type {}; 

(Ideone live code)

Hy vọng rằng, do thời gian C++ 17 đi ra, chúng tôi sẽ có thể thực hiện việc này dễ dàng hơn với Concepts Lite:

template <typename T> 
concept bool hasGetCount = requires (T t) { 
    { t.getCount() } -> int; 
}; 
+4

Mặc dù việc kiểm tra này chỉ đúng đối với các loại không phải lớp trên các lh của '=', nếu không, bạn đang kiểm tra khả năng gán. Tương tự, nó sẽ không hoạt động đối với các kiểu hàm và mảng (trực tiếp) theo như tôi thấy. – dyp

Các vấn đề liên quan