2017-07-15 14 views
5

Tôi đang sử dụng cấu trúc is_callable quy định như sauKiểm tra nếu một chức năng là callable

template <typename F, typename... Args> 
struct is_callable { 
    template <typename U> 
    static auto test(U* p) -> decltype((*p)(std::declval<Args>()...), void(), std::true_type()); 

    template <typename U> 
    static auto test(...) -> decltype(std::false_type()); 

    static constexpr bool value = decltype(test<F>(nullptr))::value; 
}; 

Tôi đang sử dụng này để thử nghiệm một lambda tuyên bố như sau:

template <typename T> 
struct runner { 
    T t; 

    template <typename F, typename = typename std::enable_if<is_callable<F, T&>::value || is_callable<F, T&&>::value>::type> 
    void run(F&& f) { 
    return f(t); 
    } 
}; 

runner<int> a{0}; 
a.run([&] (auto& x) { 
    x++; 
}); 

Tại sao điều này không biên soạn trên enable_if trên AppleClang? Không nên auto s được suy luận chính xác?

+0

[Làm việc cho tôi] (http://coliru.stacked-crooked.com/a/b83c21fddc4e15cb). Phiên bản 'clang' của bạn là gì và thông báo lỗi cụ thể là gì? – Rakete1111

+0

@ Rakete1111 'mẫu ứng cử viên bị bỏ qua: bị vô hiệu hóa bởi 'enable_if'' Apple LLVM phiên bản 8.1.0 (clang-802.0.42) – subzero

+0

Tại sao bạn kiểm tra xem nó có thể gọi với' T & '* hoặc *' T &&'? Bạn đang gọi nó bằng 'T &', chỉ cần kiểm tra điều đó. – Barry

Trả lời

1

Trường hợp của bạn cho số true_typetest có vẻ sai. Ở bất kỳ mức nào, mã của bạn phức tạp hơn mức cần thiết. Hãy thử ví dụ làm việc tối thiểu sau đây:

#include <utility> 

template<typename F, typename...Args> struct is_callable { 
    template<typename F2, typename...Args2> static constexpr std::true_type 
    test(decltype(std::declval<F2>()(std::declval<Args2>()...)) *) { return {}; } 

    template<typename F2, typename...Args2> static constexpr std::false_type 
    test(...) { return {}; } 

    static constexpr bool value = decltype(test<F, Args...>(nullptr))::value; 
}; 

void f0(); 
static_assert(is_callable<decltype(f0)>::value, "f0()"); 
static_assert(!is_callable<decltype(f0), int>::value, "f0(0)"); 

int f1(int); 
static_assert(!is_callable<decltype(f1)>::value, "f1()"); 
static_assert(is_callable<decltype(f1), int>::value, "f1(0)"); 
static_assert(!is_callable<decltype(f1), int, int>::value, "f1(0, 0)"); 

auto __attribute__((unused)) f2 = [](int, char *) { return 7; }; 
static_assert(is_callable<decltype(f2), int, char *>::value, "f2(int, char *)"); 
static_assert(!is_callable<decltype(f2), int, int>::value, "f2(int, int)"); 
+1

Đó là một * khủng khiếp * 'is_callable'. –

+0

Điều gì thật khủng khiếp? Nó hoạt động với C++ 11, 14 và 17, và sử dụng SFINAE rất rõ ràng và rõ ràng. – user3188445

0

Vấn đề không phải là lớp kiểm tra is_callable mà bạn sử dụng nó.

Khi sử dụng std::enable_if trong danh sách mẫu tranh luận của một chức năng, bạn phải sử dụng nó theo cách này:

template <typename T> 
struct runner { 
    T t; 

    template 
    < 
    typename F, 
    std::enable_if_t<is_callable<std::decay_t<F>, T&>::value || is_callable<F, T&&>::value>* = nullptr 
    > 
    void run(F&& f) { 
    return f(t); 
    } 
}; 

Mẫu mà bạn đã cố gắng sử dụng là để sử dụng như một loại dấu sự trở lại:

template<typename F> 
    auto run(F&& f) 
    -> std::enable_if_t<is_callable<std::decay_t<F>, T&>::value || is_callable<F, T&&>::value> 
    { 
    return f(t); 
    } 

Loại is_callable là chính xác. Có vẻ như tôi đã đăng lên tràn ngăn xếp trước đây.

+1

Tại sao bạn nghĩ rằng bạn * có * để sử dụng nó theo cách đó? – Barry

+0

@Barry vì đây là một trong 3 cách chính xác để bật chức năng: loại * = nullptr, loại dấu hoặc đối số sai. –

+0

Tôi không chắc tại sao bạn lại nghĩ như vậy. 'template <..., typename = std :: enable_if_t <...>>' được sử dụng khá phổ biến. – Barry

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