2016-11-18 6 views
9

Wie schreibt man ein Konzept, das die Typen beschreibt, für die die bereichsbasierte for-Schleife aktiviert ist?Wie schreibe ich ein einfaches Bereichskonzept?

Ein Versuch ist:

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

aber, was ich will wirklich etwas, was wie das ist:

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

die RRange ist, das Konzept aller Art zu sein, der Ausdruck for(auto&& item : range); gilt für . Was ist der beste Weg, dies zu erreichen?

Ich verwende GCC7 Schnappschuss mit g++ -std=c++1z -fconcepts.

Antwort

1

Hier ist, was ich bei der Überprüfung [stmt.range] kam.

#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 }; 
    }; 

Und die Testfälle.

#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

Es ist gut, einen Vorbehalt hinzufügen, dass darauf zurückzuführen, wie die Spezifikation geschrieben werden, Code Benutzer nur einen Best-Effort-Versuch Konzept-ifying machen diese Sprachfunktion (obwohl eine sehr gute). Es gibt (sehr) pathologische Randfälle, die nicht erfasst werden können. –

+0

@LucDanton Ich habe das Konzept neu geschrieben, um auf benannte Variablen zu operieren, wie es der Bereich für Semantik erfordert. Der hinzugefügte Testfall schlägt jedoch fehl, und ich weiß nicht warum. Könnte es ein Randfall sein, wie Sie zuvor erwähnt haben? –

+0

Ich hätte den neuen Testfall mit der vorherigen Lösung nicht auslassen sollen. Ich habe es durch die hinzugefügte 'declref' Indirection behoben. Danke für alles. –

0

Nach P0587, soll dies genügen:

#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>); 
} 
Verwandte Themen