2017-07-07 12 views
8

Tôi có thư viện với một số đối tượng chức năng có thể chỉ chấp nhận một vài loại tùy thuộc vào std::is_integral. Tôi muốn std::is_invocable trả lại false khi điều kiện không thành công, nhưng tôi cũng muốn có thông báo lỗi static_assert đẹp khi người dùng cố gắng gọi một phiên bản của đối tượng hàm. Dưới đây là một ví dụ đơn giản của đối tượng chức năng Tôi hiện có:Tận dụng tối đa static_assert và std :: is_invocable

struct function 
{ 
    template<typename Iterator> 
    auto operator()(Iterator first, Iterator last) const 
     -> std::enable_if_t<std::is_integral_v< 
      typename std::iterator_traits<Iterator>::value_type 
     >> 
    { /* something */ } 
}; 

Với một thực hiện như vậy, std::is_invocablestd::false_type như mong đợi khi điều kiện SFINAE không được đáp ứng, nhưng người dùng gặp phải thông báo lỗi SFINAE xấu xí khi họ cố gắng để gọi đối tượng hàm với các tham số không đáp ứng điều kiện SFINAE.

Để có được các thông báo lỗi tốt hơn, tôi đã thử giải pháp sau đây thay vì:

struct function 
{ 
    template<typename Iterator> 
    auto operator()(Iterator first, Iterator last) const 
     -> void 
    { 
     static_assert(std::is_integral_v<typename std::iterator_traits<Iterator>::value_type>, 
         "function can only be called with a collection of integers"); 

     /* something */ 
    } 
}; 

Với thực hiện này, người dùng có được các thông báo lỗi thân thiện khi điều kiện SFINAE gốc không được đáp ứng, nhưng std::is_invocablestd::true_type khi được hỏi liệu một Ví dụ function có thể xử lý một loại không đáp ứng std::is_integral.

Tôi đã thử nhiều thủ đoạn và các biến thể liên quan đến decltype(auto), if constexpr và các cơ chế khác, nhưng không thể có được một lớp học nơi thông báo lỗi là tốt đẹp và nơi std::is_invocable tương ứng với dự kiến ​​std::false_type khi đặt câu hỏi liệu function có thể được gọi với các loại không chính xác.

tôi thiếu gì ở đây? Có cách nào để nhận cả hai thông báo lỗi thân thiện với người dùng std::is_invocable thân thiện với người dùng?

+0

Tôi vừa mới viết một bài đăng blog về điều này: https://gracicot.github.io/tricks/2017/07/01/deleted-function-diagnostic.html –

Trả lời

7

Đây là một cách khủng khiếp. Thêm một tình trạng quá tải:

template <typename Iterator> 
auto operator()(Iterator first, Iterator last) const 
    -> std::enable_if_t<std::is_integral_v< 
     typename std::iterator_traits<Iterator>::value_type 
    >>; 

template <typename Iterator, class... Args> 
void operator()(Iterator, Iterator, Args&&...) const = delete; // must be integral 

này đáp ứng các điều kiện is_invocable<>, vì chức năng template contrained là chuyên biệt hơn và ưa thích - vì vậy nếu điều kiện được đáp ứng, chức năng là invocable, nếu không nó bị xóa.

này không tốt hơn một chút về trường hợp lỗi, vì nếu bạn cố gắng gọi nó, bạn nhận được:

foo.cxx: In function ‘int main()’: 
foo.cxx:31:13: error: use of deleted function ‘void function::operator()(Iterator, Iterator, Args&& ...) const [with Iterator = std::__cxx11::basic_string<char>*; Args = {}]’ 
    f(&i, &i); 
      ^
foo.cxx:19:10: note: declared here 
    void operator()(Iterator, Iterator, Args&&...) const = delete; // must be integral 
      ^~~~~~~~ 

Các bình luận xuất hiện trong thông báo lỗi, đó là loại giống như một khẳng định tĩnh thông điệp?


Đây thực sự là một trong những động lực cho các khái niệm. Với requires thay vì một enable_if, chúng tôi nhận được:

foo.cxx: In function ‘int main()’: 
foo.cxx:26:13: error: no match for call to ‘(function) (std::__cxx11::string*, std::__cxx11::string*)’ 
    f(&i, &i); 
      ^
foo.cxx:11:10: note: candidate: void function::operator()(Iterator, Iterator) const requires is_integral_v<typename std::iterator_traits<_Iter>::value_type> [with Iterator = std::__cxx11::basic_string<char>*] 
    void operator()(Iterator first, Iterator last) const 
      ^~~~~~~~ 
foo.cxx:11:10: note: constraints not satisfied 
foo.cxx:11:10: note: ‘is_integral_v<typename std::iterator_traits<_Iter>::value_type>’ evaluated to false 

... Đó là một chút tốt hơn tôi đoán.

+0

Các giải pháp khái niệm thực sự tốt hơn một chút. Có những nơi khác khi tôi cần hành vi SFINAE, tôi cần kiểm tra xem '= delete' có khả thi trong trường hợp của tôi hay không, nhưng đó có thể là một con đường thú vị để khám phá. – Morwenn

+4

Tôi đã tìm thấy [một bài viết] (https://gracicot.github.io/tricks/2017/07/01/deleted-function-diagnostic.html) cải thiện thủ thuật '= delete', cho phép cung cấp một' chuyên dụng ' thông điệp tĩnh_assert', không tệ. – Morwenn

+0

@Morwenn Tôi tự hỏi nếu tác giả của bài viết là đúng ... Tôi đọc bạn ** không thể ** tạo mẫu mà sẽ không có bất kỳ chuyên môn hợp lệ và 'static_assert (! Std :: is_same_v ," message ")' sẽ phá vỡ quy tắc đó làm cho mã không đúng định dạng mà không cần chẩn đoán lại. –

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