2017-08-22 15 views
6

Có cách nào để sử dụng detection idiom (hoặc phương pháp khác) để kiểm tra xem một hàm có hợp lệ đối với các đối số mẫu đã cho hay không, nếu nó không thành công do static_assert?Phát hiện thành ngữ có chức năng không static_assert

Ví dụ dưới đây minh họa rằng tính hợp lệ của foo (không tính toán loại trả về) được phát hiện như dự định, nhưng của bar (không static_assert) thì không.

#include <iostream> 
#include <type_traits> 

template <typename... T> using void_t = void; 

template <class AlwaysVoid, template<class...> class Op, class... Args> 
struct detector: std::false_type { }; 

template <template<class...> class Op, class... Args> 
struct detector<void_t<Op<Args...>>, Op, Args...>: std::true_type { }; 

template <template<class...> class Op, class... Args> 
constexpr bool is_detected = detector<void, Op, Args...>::value; 

template <typename T> 
std::enable_if_t<!std::is_void<T>::value> foo() { 
    std::cout << "foo" << std::endl; 
} 

template <typename T> 
void bar() { 
    static_assert(!std::is_void<T>::value); 
    std::cout << "bar" << std::endl; 
} 

template <typename T> using foo_t = decltype(foo<T>()); 
template <typename T> using bar_t = decltype(bar<T>()); 

int main(int argc, char* argv[]) { 

    foo<int>(); 
    // foo<void>(); // fails as expected 

    bar<int>(); 
    // bar<void>(); // fails as expected 

    std::cout << std::boolalpha; 

    // detection works for foo 
    std::cout << is_detected<foo_t,int > << std::endl; // true 
    std::cout << is_detected<foo_t,void> << std::endl; // false 

    // but not for bar 
    std::cout << is_detected<bar_t,int > << std::endl; // true 
    std::cout << is_detected<bar_t,void> << std::endl; // true !!! 
} 

Đây là lý do tôi không thể phát hiện nếu một boost::lexical_cast hợp lệ cho các loại nhất định.

Trả lời

6

Không thể sử dụng SFINAE để có được kết quả phù hợp tại đây, vì SFINAE rules hoạt động trên các tờ khai , không phải định nghĩa.

Loại bar như đã khai báo sẽ luôn là void(void), vì vậy việc khai báo không quan trọng bằng SFINAE.

Nếu bạn viết lên một thành ngữ phát hiện thực (Giống như I did here), và sử dụng nó như vậy:

template <typename T> 
using CanCallFoo_t = decltype(&foo<T>); 

template<class T> 
using CanCallFoo = detect<T, CanCallFoo_t, void>; 

template<class T> 
using CanCallBar_t = decltype(&bar<T>); 

template< class T> 
using 
CanCallBar = detect<T, CanCallBar_t, void>; 

//... 
std::cout << CanCallFoo<int>::value << std::endl; // true 
std::cout << CanCallFoo<void>::value << std::endl; // false 

std::cout << CanCallBar<int>::value << std::endl; 
std::cout << CanCallBar<void>::value << std::endl; 

Bạn sẽ nhận thấy rằng SFINAE thành công và sau đó bạn nhận được một lỗi biên dịch khi định nghĩa được phân tách.

error: static assertion failed
static_assert(!std::is_void<T>::value);

Demo

Chú ý rằng nó hoạt động với foofoo 's loại sẽ thất bại SFINAE cho void

Mấu chốt của static_assert là làm cho biên soạn thất bại nếu không phù hợp khác tốt hơn được tìm thấy tuyên bố, không phải là thay thế cho SFINAE.

1

Câu trả lời của Andy là đúng, trong việc giải thích rằng việc phát hiện static_assert hoặc bất kỳ lỗi thay thế định nghĩa nào là không thể với sfinae. Tôi muốn chỉ ra rằng, có thể giải quyết vấn đề của bạn, mặc dù bạn sẽ cần phải chịu đựng gánh nặng của một số sự lặp lại.

Về cơ bản, bạn cần tìm hiểu xem loại hoạt động nào lexical_cast đang cố gắng áp dụng cho loại chung. Sau đó, bạn quấn lexical_cast với chức năng của riêng bạn và sfinae chức năng của bạn trên bất kỳ thuộc tính nào lexical_cast yêu cầu. Đây không phải là thanh lịch nhưng tôi nghi ngờ có nhiều hơn một vài yêu cầu về loại có liên quan đến ứng dụng của bạn quan tâm, vì vậy nó là một giải pháp thiết thực (có lẽ).

Có thể điều này là hiển nhiên nhưng tôi nghĩ tôi sẽ đề cập đến điều này vì nó chưa được đề cập đến.

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