如何编写一个概念来描述基于范围的for循环的类型?
一次尝试是:
template < typename Range > concept bool RRange
= requires(Range range) {{std::begin(range),std::end(range)};};
但我真正想要的是这样的事情:
template < typename Range > concept bool RRange
= requires(Range range) {{for(auto&& item : range);};}; // compile error
即,RRange
是表达式for(auto&& item : range);
有效的所有类型的概念。实现这一目标的最佳方法是什么?
我正在使用带有g++ -std=c++1z -fconcepts
的GCC7快照。
答案 0 :(得分:1)
以下是我在审核[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 };
};
和测试用例。
#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 :(得分:0)
根据P0587,这应该足够了:
#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>);
}
答案 2 :(得分:0)
在 C++20 中,它看起来像这样:
template< class T >
concept RealContainer = requires(T&& t) {
std::begin(std::forward<T>(t));
std::end (std::forward<T>(t));
};
可能并不完美,但适用于 std::vector 和 C 数组,演示:https://gcc.godbolt.org/z/M4xhnqG46