2016-11-18 16 views
9

Làm thế nào để viết một khái niệm sẽ mô tả các loại dựa trên phạm vi cho vòng lặp được kích hoạt cho?Làm thế nào để viết một khái niệm phạm vi đơn giản?

Một nỗ lực là:

template < typename Range > concept bool RRange 
    = requires(Range range) {{std::begin(range),std::end(range)};}; 

nhưng những gì tôi thực sự muốn là một số điều như thế này:

template < typename Range > concept bool RRange 
    = requires(Range range) {{for(auto&& item : range);};}; // compile error 

có nghĩa là, RRange được các khái niệm về tất cả các loại biểu thức for(auto&& item : range); có giá trị . cách tốt nhất để đạt được điều này là gì?

Tôi đang sử dụng ảnh chụp GCC7 với g++ -std=c++1z -fconcepts.

Trả lời

1

Đây là những gì tôi đã đưa ra khi xem xét [stmt.ranged].

#include <utility> 
#include <experimental/type_traits> 

template <class T> using begin_non_mf_t = decltype(begin(std::declval<T>())); 
template <class T> using begin_mf_t  = decltype(std::declval<T>().begin()); 
template <class T> using begin_t  = decltype(T::begin); 
template <class T> using end_non_mf_t = decltype(end(std::declval<T>())); 
template <class T> using end_mf_t  = decltype(std::declval<T>().end()); 
template <class T> using end_t   = decltype(T::end); 

template <class T> 
constexpr bool has_member_begin_or_end { 
    std::experimental::is_detected_v<begin_mf_t,T> || 
    std::experimental::is_detected_v<begin_t,T> || 
    std::experimental::is_detected_v<end_mf_t,T> || 
    std::experimental::is_detected_v<end_t,T>}; 

template <class T> 
std::add_lvalue_reference_t<T> declref() noexcept; 
template <class T> using declref_t = decltype(declref<T>()); 

template <class T> 
concept bool Range = 
    requires /*Arrays*/ { 
     requires std::is_array_v<T>; 
     requires std::extent_v<T>!=0; // Extent is known. 
    } || 
    /*Classes with member begin/end*/ 
    requires { 
     requires std::is_class_v<T> && has_member_begin_or_end<T>; 
    } && 
    requires (begin_mf_t<declref_t<T>> _begin, 
       end_mf_t<declref_t<T>> _end) { 
     { _begin!=_end } -> bool; 
     { *_begin } -> auto&&; 
     { ++_begin }; 
    } || 
    /*Types with non-member begin/end*/ 
    requires { 
     requires !std::is_class_v<T> || !has_member_begin_or_end<T>; 
    } && 
    requires (begin_non_mf_t<declref_t<T>> _begin, 
       end_non_mf_t<declref_t<T>> _end) { 
     { _begin!=_end } -> bool; 
     { *_begin } -> auto&&; 
     { ++_begin }; 
    }; 

Và các trường hợp kiểm tra.

#include <vector> 

// Evaluates to true or diagnoses which constraints failed. 
template <Range> constexpr bool is_range {true}; 

static_assert(!Range<void>); 
static_assert(!Range<int>); 
static_assert(!Range<int*>); 
static_assert(!Range<int[]>); 
static_assert(is_range<int[1]>); 
static_assert(is_range<std::vector<int>>); 

struct A { }; 
struct B { 
    int begin; 
}; 
struct C { 
    int* begin(); 
    int* end(); 
}; 
struct D { }; 
struct E { 
    int end; 
}; 
enum F { }; 
struct G { 
    int* begin() &&; 
    int* end(); 
}; 
struct H { 
    int* begin() &&; 
    int* end() &&; 
}; 
int* begin(D); 
int* end(D); 
int* begin(E); 
int* end(E); 
int* begin(F); 
int* end(F); 
int* begin(H); 
int* end(H); 

static_assert(!Range<A>); 
static_assert(!Range<B>); 
static_assert(is_range<C>); 
static_assert(is_range<D>); 
static_assert(!Range<E>); 
static_assert(is_range<F>); 
static_assert(!Range<G>); 
static_assert(!Range<H>); 

int main() { } 
+1

Đó là tốt để thêm một cảnh báo rằng do cách spec được viết, mã người dùng chỉ có thể làm cho một nỗ lực nỗ lực tốt nhất tại khái niệm-ifying tính năng ngôn ngữ này (mặc dù một tính năng rất tốt). Có (rất) trường hợp cạnh bệnh lý không thể bị bắt. –

+0

@LucDanton Tôi đã viết lại khái niệm để hoạt động trên các biến có tên, theo yêu cầu của các ngữ nghĩa cho phạm vi. Tuy nhiên, trường hợp kiểm tra bổ sung không thành công và tôi không biết tại sao. Nó có thể là một trường hợp cạnh, như bạn đã đề cập trước đó? –

+0

Tôi không nên bỏ qua kiểm tra trường hợp thử nghiệm mới với giải pháp trước đó. Tôi đã sửa nó thông qua sự thừa hưởng 'declref' được thêm vào. Cảm ơn mọi thứ. –

0

Theo P0587, điều này sẽ đủ:

#include <vector> 

template<typename T> 
concept bool RangeForAble = requires (T t) { 
    requires requires (decltype(begin(t)) b, decltype(end(t)) e) { 
    b != e; 
    ++b; 
    *b; 
    }; 
}; 

int main() 
{ 
static_assert(RangeForAble<std::vector<int>>); 
static_assert(RangeForAble<double>); 
} 
Các vấn đề liên quan