2015-10-27 15 views
10

Tôi đã sử dụng biểu thức SFINAE để kiểm tra nếu một loại hỗ trợ operator<<Có cách nào để viết một thử nghiệm SFINAE về "tính khả thi" của một loại không?

namespace details 
{ 
    template<typename T> 
    struct sfinae_true : std::true_type 
    { 
    }; 

    template<typename T> 
    sfinae_true<decltype (std::declval<std::ostream &>() << std::declval<T const &>())> test_for_ostream (int); 

    template<typename T> 
    std::false_type test_for_ostream (long); 
} 

template<typename T> 
struct supports_ostream : decltype (details::test_for_ostream<T> (0)) 
{ 
}; 

Những gì tôi muốn thử nghiệm là nếu điều này một loại T có thể được lặp qua như thế này

for (auto && v : vs) {} // vs is T const & 

Các tiến thoái lưỡng nan là rằng đây là một tuyên bố và không phải là một biểu thức làm cho nó không tương thích để sử dụng với decltype

Tôi đã suy nghĩ để sử dụng lambdas để chuyển đổi một câu lệnh thành biểu thức ion như thế này

auto x = []() { for (auto && v : vs) {}; return 0; }(); // vs is T const & 

Tuy nhiên decltype các biểu thức chứa lambdas dường như bị cấm một cách rõ ràng:

// Won't compile in clang, gcc nor VC++ 
using x_t = decltype ([]() { for (auto && v : vs) {}; return 0; }()); // vs is T const & 

Vì vậy mà loại khách nó để sử dụng trong một chức năng kiểm tra như thế này:

namespace details 
{ 
    template<typename T> 
    sfinae_true<decltype (
    []() { for (auto && v : std::declval<T const &>()) ; }() 
    )> test_for_container (int); 
    // Won't work because lambdas aren't allowed in unevaluated contexts 

    template<typename T> 
    std::false_type test_for_container (long); 
} 

template<typename T> 
struct is_container : decltype (details::test_for_container<T> (0)) 
{ 
}; 

Vì vậy, Tôi đã hết ý tưởng, vì vậy tôi nghĩ có lẽ ai đó @Stackoverflow có thể nghĩ ra điều gì đó thú vị.

PS.

Tôi có thể hiểu được lý do tại sao decltype ([]() {}) không được phép nhưng decltype ([]() {}()) phải luôn được xác định rõ ràng tức là void.

+1

FWIW, câu trả lời của tôi [ở đây] (https://stackoverflow.com/questions/32293860/mô phỏng-phạm vi-dựa-cho-vòng-bắt đầu-kết thúc hành vi) là SFINAE thân thiện, tức là kiểm tra hiệu lực cuộc gọi sẽ làm việc. – Columbo

+0

Xin chào, cảm ơn. Rằng sẽ làm việc. Cú đâm của tôi ở nó trông hơi giống nhưng đã bỏ lỡ một số trường hợp. – FuleSnabel

Trả lời

5

Đối với phần lớn các trường hợp, các đặc điểm sau đây cần đủ:

#include <type_traits> 
#include <utility> 
#include <iterator> 

namespace detail 
{ 
    using std::begin; 
    using std::end; 

    template <typename T> 
    auto is_range_based_iterable(...) 
     -> std::false_type; 

    template <typename T 
      , typename I = typename std::decay<decltype(std::declval<T>().begin())>::type> 
    auto is_range_based_iterable(int) 
     -> decltype(std::declval<T>().begin() 
        , std::declval<T>().end() 
        , ++std::declval<I&>() 
        , void() 
        , std::integral_constant<bool, 
         std::is_convertible<decltype(std::declval<I&>() != std::declval<I&>()), bool>::value 
        && !std::is_void<decltype(*std::declval<I&>())>::value 
        && std::is_copy_constructible<I>::value 
        >{}); 

    template <typename T 
      , typename I = typename std::decay<decltype(begin(std::declval<T>()))>::type> 
    auto is_range_based_iterable(char) 
     -> decltype(begin(std::declval<T>()) 
        , end(std::declval<T>()) 
        , ++std::declval<I&>() 
        , void() 
        , std::integral_constant<bool, 
         std::is_convertible<decltype(std::declval<I&>() != std::declval<I&>()), bool>::value 
        && !std::is_void<decltype(*std::declval<I&>())>::value 
        && std::is_copy_constructible<I>::value 
        >{}); 
} 

template <typename T> 
struct is_range_based_iterable : decltype(detail::is_range_based_iterable<T>(0)) {}; 

Test:

#include <vector> 
#include <array> 

int main() 
{ 
    static_assert(is_range_based_iterable<std::vector<int>>::value, "!"); 
    static_assert(is_range_based_iterable<std::array<int, 5>>::value, "!"); 
    static_assert(is_range_based_iterable<int(&)[5]>::value, "!"); 
} 

DEMO

+2

Bạn không cần kiểm tra các thành viên '.begin()' /'.end() ', việc tra cứu nhận biết ADL của' begin() '/' end() 'với các phiên bản' std' trong phạm vi xử lý độc đáo cho bạn. –

+0

Xin chào. Điều này trả lời các câu hỏi. Tôi nghĩ rằng tôi đã bí mật muốn một ngày nào đó sử dụng câu lệnh 'for' để kiểm tra nó. – FuleSnabel

+1

@LucDanton No. Tôi có thể tưởng tượng bản thân mình [một thùng chứa] (http://coliru.stacked-crooked.com/a/2a13665c7c84235d) có thể lặp lại thông qua các hàm thành viên 'begin()'/'end()', và có namespace định nghĩa 'begin()' và 'end()', khi được tìm thấy thông qua ADL, sẽ không cho phép bạn liệt kê các phần tử của thùng chứa –

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