ee22bc201e
Signed-off-by: Jonathan Wakely <jwakely@redhat.com>
libstdc++-v3/ChangeLog:
* include/bits/ranges_base.h (ranges::empty): Check whether
conversion to bool can throw.
* testsuite/std/ranges/access/empty.cc: Check for correct
noexcept-specifier.
(cherry picked from commit f9598d89a9
)
901 lines
24 KiB
C++
901 lines
24 KiB
C++
// Core concepts and definitions for <ranges> -*- C++ -*-
|
|
|
|
// Copyright (C) 2019-2021 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// Under Section 7 of GPL version 3, you are granted additional
|
|
// permissions described in the GCC Runtime Library Exception, version
|
|
// 3.1, as published by the Free Software Foundation.
|
|
|
|
// You should have received a copy of the GNU General Public License and
|
|
// a copy of the GCC Runtime Library Exception along with this program;
|
|
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
/** @file bits/ranges_base.h
|
|
* This is an internal header file, included by other library headers.
|
|
* Do not attempt to use it directly. @headername{ranges}
|
|
*/
|
|
|
|
#ifndef _GLIBCXX_RANGES_BASE_H
|
|
#define _GLIBCXX_RANGES_BASE_H 1
|
|
|
|
#pragma GCC system_header
|
|
|
|
#if __cplusplus > 201703L
|
|
#include <bits/iterator_concepts.h>
|
|
#include <ext/numeric_traits.h>
|
|
#include <bits/max_size_type.h>
|
|
|
|
#ifdef __cpp_lib_concepts
|
|
namespace std _GLIBCXX_VISIBILITY(default)
|
|
{
|
|
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|
namespace ranges
|
|
{
|
|
template<typename>
|
|
inline constexpr bool disable_sized_range = false;
|
|
|
|
template<typename _Tp>
|
|
inline constexpr bool enable_borrowed_range = false;
|
|
|
|
namespace __detail
|
|
{
|
|
constexpr __max_size_type
|
|
__to_unsigned_like(__max_size_type __t) noexcept
|
|
{ return __t; }
|
|
|
|
constexpr __max_size_type
|
|
__to_unsigned_like(__max_diff_type __t) noexcept
|
|
{ return __max_size_type(__t); }
|
|
|
|
template<integral _Tp>
|
|
constexpr auto
|
|
__to_unsigned_like(_Tp __t) noexcept
|
|
{ return static_cast<make_unsigned_t<_Tp>>(__t); }
|
|
|
|
#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
|
|
constexpr unsigned __int128
|
|
__to_unsigned_like(__int128 __t) noexcept
|
|
{ return __t; }
|
|
|
|
constexpr unsigned __int128
|
|
__to_unsigned_like(unsigned __int128 __t) noexcept
|
|
{ return __t; }
|
|
#endif
|
|
|
|
template<typename _Tp>
|
|
using __make_unsigned_like_t
|
|
= decltype(__detail::__to_unsigned_like(std::declval<_Tp>()));
|
|
|
|
// Part of the constraints of ranges::borrowed_range
|
|
template<typename _Tp>
|
|
concept __maybe_borrowed_range
|
|
= is_lvalue_reference_v<_Tp>
|
|
|| enable_borrowed_range<remove_cvref_t<_Tp>>;
|
|
|
|
} // namespace __detail
|
|
|
|
namespace __cust_access
|
|
{
|
|
using std::ranges::__detail::__maybe_borrowed_range;
|
|
|
|
struct _Begin
|
|
{
|
|
private:
|
|
template<typename _Tp>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (is_array_v<remove_reference_t<_Tp>>)
|
|
return true;
|
|
else if constexpr (__member_begin<_Tp>)
|
|
return noexcept(__decay_copy(std::declval<_Tp&>().begin()));
|
|
else
|
|
return noexcept(__decay_copy(begin(std::declval<_Tp&>())));
|
|
}
|
|
|
|
public:
|
|
template<__maybe_borrowed_range _Tp>
|
|
requires is_array_v<remove_reference_t<_Tp>> || __member_begin<_Tp>
|
|
|| __adl_begin<_Tp>
|
|
constexpr auto
|
|
operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp&>())
|
|
{
|
|
if constexpr (is_array_v<remove_reference_t<_Tp>>)
|
|
{
|
|
static_assert(is_lvalue_reference_v<_Tp>);
|
|
return __t + 0;
|
|
}
|
|
else if constexpr (__member_begin<_Tp>)
|
|
return __t.begin();
|
|
else
|
|
return begin(__t);
|
|
}
|
|
};
|
|
|
|
template<typename _Tp>
|
|
concept __member_end = requires(_Tp& __t)
|
|
{
|
|
{ __decay_copy(__t.end()) }
|
|
-> sentinel_for<decltype(_Begin{}(std::forward<_Tp>(__t)))>;
|
|
};
|
|
|
|
// Poison pills so that unqualified lookup doesn't find std::end.
|
|
void end(auto&) = delete;
|
|
void end(const auto&) = delete;
|
|
|
|
template<typename _Tp>
|
|
concept __adl_end = __class_or_enum<remove_reference_t<_Tp>>
|
|
&& requires(_Tp& __t)
|
|
{
|
|
{ __decay_copy(end(__t)) }
|
|
-> sentinel_for<decltype(_Begin{}(std::forward<_Tp>(__t)))>;
|
|
};
|
|
|
|
struct _End
|
|
{
|
|
private:
|
|
template<typename _Tp>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
|
|
return true;
|
|
else if constexpr (__member_end<_Tp>)
|
|
return noexcept(__decay_copy(std::declval<_Tp&>().end()));
|
|
else
|
|
return noexcept(__decay_copy(end(std::declval<_Tp&>())));
|
|
}
|
|
|
|
public:
|
|
template<__maybe_borrowed_range _Tp>
|
|
requires is_bounded_array_v<remove_reference_t<_Tp>>
|
|
|| __member_end<_Tp> || __adl_end<_Tp>
|
|
constexpr auto
|
|
operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp&>())
|
|
{
|
|
if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
|
|
{
|
|
static_assert(is_lvalue_reference_v<_Tp>);
|
|
return __t + extent_v<remove_reference_t<_Tp>>;
|
|
}
|
|
else if constexpr (__member_end<_Tp>)
|
|
return __t.end();
|
|
else
|
|
return end(__t);
|
|
}
|
|
};
|
|
|
|
// If _To is an lvalue-reference, return const _Tp&, otherwise const _Tp&&.
|
|
template<typename _To, typename _Tp>
|
|
constexpr decltype(auto)
|
|
__as_const(_Tp& __t) noexcept
|
|
{
|
|
static_assert(std::is_same_v<_To&, _Tp&>);
|
|
|
|
if constexpr (is_lvalue_reference_v<_To>)
|
|
return const_cast<const _Tp&>(__t);
|
|
else
|
|
return static_cast<const _Tp&&>(__t);
|
|
}
|
|
|
|
struct _CBegin
|
|
{
|
|
template<typename _Tp>
|
|
constexpr auto
|
|
operator()(_Tp&& __e) const
|
|
noexcept(noexcept(_Begin{}(__cust_access::__as_const<_Tp>(__e))))
|
|
requires requires { _Begin{}(__cust_access::__as_const<_Tp>(__e)); }
|
|
{
|
|
return _Begin{}(__cust_access::__as_const<_Tp>(__e));
|
|
}
|
|
};
|
|
|
|
struct _CEnd
|
|
{
|
|
template<typename _Tp>
|
|
constexpr auto
|
|
operator()(_Tp&& __e) const
|
|
noexcept(noexcept(_End{}(__cust_access::__as_const<_Tp>(__e))))
|
|
requires requires { _End{}(__cust_access::__as_const<_Tp>(__e)); }
|
|
{
|
|
return _End{}(__cust_access::__as_const<_Tp>(__e));
|
|
}
|
|
};
|
|
|
|
template<typename _Tp>
|
|
concept __member_rbegin = requires(_Tp& __t)
|
|
{
|
|
{ __decay_copy(__t.rbegin()) } -> input_or_output_iterator;
|
|
};
|
|
|
|
void rbegin(auto&) = delete;
|
|
void rbegin(const auto&) = delete;
|
|
|
|
template<typename _Tp>
|
|
concept __adl_rbegin = __class_or_enum<remove_reference_t<_Tp>>
|
|
&& requires(_Tp& __t)
|
|
{
|
|
{ __decay_copy(rbegin(__t)) } -> input_or_output_iterator;
|
|
};
|
|
|
|
template<typename _Tp>
|
|
concept __reversable = requires(_Tp& __t)
|
|
{
|
|
{ _Begin{}(__t) } -> bidirectional_iterator;
|
|
{ _End{}(__t) } -> same_as<decltype(_Begin{}(__t))>;
|
|
};
|
|
|
|
struct _RBegin
|
|
{
|
|
private:
|
|
template<typename _Tp>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (__member_rbegin<_Tp>)
|
|
return noexcept(__decay_copy(std::declval<_Tp&>().rbegin()));
|
|
else if constexpr (__adl_rbegin<_Tp>)
|
|
return noexcept(__decay_copy(rbegin(std::declval<_Tp&>())));
|
|
else
|
|
{
|
|
if constexpr (noexcept(_End{}(std::declval<_Tp&>())))
|
|
{
|
|
using _It = decltype(_End{}(std::declval<_Tp&>()));
|
|
// std::reverse_iterator copy-initializes its member.
|
|
return is_nothrow_copy_constructible_v<_It>;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public:
|
|
template<__maybe_borrowed_range _Tp>
|
|
requires __member_rbegin<_Tp> || __adl_rbegin<_Tp> || __reversable<_Tp>
|
|
constexpr auto
|
|
operator()(_Tp&& __t) const
|
|
noexcept(_S_noexcept<_Tp&>())
|
|
{
|
|
if constexpr (__member_rbegin<_Tp>)
|
|
return __t.rbegin();
|
|
else if constexpr (__adl_rbegin<_Tp>)
|
|
return rbegin(__t);
|
|
else
|
|
return std::make_reverse_iterator(_End{}(__t));
|
|
}
|
|
};
|
|
|
|
template<typename _Tp>
|
|
concept __member_rend = requires(_Tp& __t)
|
|
{
|
|
{ __decay_copy(__t.rend()) }
|
|
-> sentinel_for<decltype(_RBegin{}(__t))>;
|
|
};
|
|
|
|
void rend(auto&) = delete;
|
|
void rend(const auto&) = delete;
|
|
|
|
template<typename _Tp>
|
|
concept __adl_rend = __class_or_enum<remove_reference_t<_Tp>>
|
|
&& requires(_Tp& __t)
|
|
{
|
|
{ __decay_copy(rend(__t)) }
|
|
-> sentinel_for<decltype(_RBegin{}(std::forward<_Tp>(__t)))>;
|
|
};
|
|
|
|
struct _REnd
|
|
{
|
|
private:
|
|
template<typename _Tp>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (__member_rend<_Tp>)
|
|
return noexcept(__decay_copy(std::declval<_Tp&>().rend()));
|
|
else if constexpr (__adl_rend<_Tp>)
|
|
return noexcept(__decay_copy(rend(std::declval<_Tp&>())));
|
|
else
|
|
{
|
|
if constexpr (noexcept(_Begin{}(std::declval<_Tp&>())))
|
|
{
|
|
using _It = decltype(_Begin{}(std::declval<_Tp&>()));
|
|
// std::reverse_iterator copy-initializes its member.
|
|
return is_nothrow_copy_constructible_v<_It>;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public:
|
|
template<__maybe_borrowed_range _Tp>
|
|
requires __member_rend<_Tp> || __adl_rend<_Tp> || __reversable<_Tp>
|
|
constexpr auto
|
|
operator()(_Tp&& __t) const
|
|
noexcept(_S_noexcept<_Tp&>())
|
|
{
|
|
if constexpr (__member_rend<_Tp>)
|
|
return __t.rend();
|
|
else if constexpr (__adl_rend<_Tp>)
|
|
return rend(__t);
|
|
else
|
|
return std::make_reverse_iterator(_Begin{}(__t));
|
|
}
|
|
};
|
|
|
|
struct _CRBegin
|
|
{
|
|
template<typename _Tp>
|
|
constexpr auto
|
|
operator()(_Tp&& __e) const
|
|
noexcept(noexcept(_RBegin{}(__cust_access::__as_const<_Tp>(__e))))
|
|
requires requires { _RBegin{}(__cust_access::__as_const<_Tp>(__e)); }
|
|
{
|
|
return _RBegin{}(__cust_access::__as_const<_Tp>(__e));
|
|
}
|
|
};
|
|
|
|
struct _CREnd
|
|
{
|
|
template<typename _Tp>
|
|
constexpr auto
|
|
operator()(_Tp&& __e) const
|
|
noexcept(noexcept(_REnd{}(__cust_access::__as_const<_Tp>(__e))))
|
|
requires requires { _REnd{}(__cust_access::__as_const<_Tp>(__e)); }
|
|
{
|
|
return _REnd{}(__cust_access::__as_const<_Tp>(__e));
|
|
}
|
|
};
|
|
|
|
template<typename _Tp>
|
|
concept __member_size = !disable_sized_range<remove_cvref_t<_Tp>>
|
|
&& requires(_Tp& __t)
|
|
{
|
|
{ __decay_copy(__t.size()) } -> __detail::__is_integer_like;
|
|
};
|
|
|
|
void size(auto&) = delete;
|
|
void size(const auto&) = delete;
|
|
|
|
template<typename _Tp>
|
|
concept __adl_size = __class_or_enum<remove_reference_t<_Tp>>
|
|
&& !disable_sized_range<remove_cvref_t<_Tp>>
|
|
&& requires(_Tp& __t)
|
|
{
|
|
{ __decay_copy(size(__t)) } -> __detail::__is_integer_like;
|
|
};
|
|
|
|
template<typename _Tp>
|
|
concept __sentinel_size = requires(_Tp& __t)
|
|
{
|
|
{ _Begin{}(__t) } -> forward_iterator;
|
|
|
|
{ _End{}(__t) } -> sized_sentinel_for<decltype(_Begin{}(__t))>;
|
|
|
|
__detail::__to_unsigned_like(_End{}(__t) - _Begin{}(__t));
|
|
};
|
|
|
|
struct _Size
|
|
{
|
|
private:
|
|
template<typename _Tp>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
|
|
return true;
|
|
else if constexpr (__member_size<_Tp>)
|
|
return noexcept(__decay_copy(std::declval<_Tp&>().size()));
|
|
else if constexpr (__adl_size<_Tp>)
|
|
return noexcept(__decay_copy(size(std::declval<_Tp&>())));
|
|
else if constexpr (__sentinel_size<_Tp>)
|
|
return noexcept(_End{}(std::declval<_Tp&>())
|
|
- _Begin{}(std::declval<_Tp&>()));
|
|
}
|
|
|
|
public:
|
|
template<typename _Tp>
|
|
requires is_bounded_array_v<remove_reference_t<_Tp>>
|
|
|| __member_size<_Tp> || __adl_size<_Tp> || __sentinel_size<_Tp>
|
|
constexpr auto
|
|
operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp&>())
|
|
{
|
|
if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
|
|
return extent_v<remove_reference_t<_Tp>>;
|
|
else if constexpr (__member_size<_Tp>)
|
|
return __t.size();
|
|
else if constexpr (__adl_size<_Tp>)
|
|
return size(__t);
|
|
else if constexpr (__sentinel_size<_Tp>)
|
|
return __detail::__to_unsigned_like(_End{}(__t) - _Begin{}(__t));
|
|
}
|
|
};
|
|
|
|
struct _SSize
|
|
{
|
|
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
|
// 3403. Domain of ranges::ssize(E) doesn't match ranges::size(E)
|
|
template<typename _Tp>
|
|
requires requires (_Tp& __t) { _Size{}(__t); }
|
|
constexpr auto
|
|
operator()(_Tp&& __t) const noexcept(noexcept(_Size{}(__t)))
|
|
{
|
|
auto __size = _Size{}(__t);
|
|
using __size_type = decltype(__size);
|
|
// Return the wider of ptrdiff_t and make-signed-like-t<__size_type>.
|
|
if constexpr (integral<__size_type>)
|
|
{
|
|
using __gnu_cxx::__int_traits;
|
|
if constexpr (__int_traits<__size_type>::__digits
|
|
< __int_traits<ptrdiff_t>::__digits)
|
|
return static_cast<ptrdiff_t>(__size);
|
|
else
|
|
return static_cast<make_signed_t<__size_type>>(__size);
|
|
}
|
|
#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
|
|
// For strict-ansi modes integral<__int128> is false
|
|
else if constexpr (__detail::__is_int128<__size_type>)
|
|
return static_cast<__int128>(__size);
|
|
#endif
|
|
else // Must be one of __max_diff_type or __max_size_type.
|
|
return __detail::__max_diff_type(__size);
|
|
}
|
|
};
|
|
|
|
template<typename _Tp>
|
|
concept __member_empty = requires(_Tp& __t) { bool(__t.empty()); };
|
|
|
|
template<typename _Tp>
|
|
concept __size0_empty = requires(_Tp& __t) { _Size{}(__t) == 0; };
|
|
|
|
template<typename _Tp>
|
|
concept __eq_iter_empty = requires(_Tp& __t)
|
|
{
|
|
{ _Begin{}(__t) } -> forward_iterator;
|
|
|
|
bool(_Begin{}(__t) == _End{}(__t));
|
|
};
|
|
|
|
struct _Empty
|
|
{
|
|
private:
|
|
template<typename _Tp>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (__member_empty<_Tp>)
|
|
return noexcept(bool(std::declval<_Tp&>().empty()));
|
|
else if constexpr (__size0_empty<_Tp>)
|
|
return noexcept(_Size{}(std::declval<_Tp&>()) == 0);
|
|
else
|
|
return noexcept(bool(_Begin{}(std::declval<_Tp&>())
|
|
== _End{}(std::declval<_Tp&>())));
|
|
}
|
|
|
|
public:
|
|
template<typename _Tp>
|
|
requires __member_empty<_Tp> || __size0_empty<_Tp>
|
|
|| __eq_iter_empty<_Tp>
|
|
constexpr bool
|
|
operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp&>())
|
|
{
|
|
if constexpr (__member_empty<_Tp>)
|
|
return bool(__t.empty());
|
|
else if constexpr (__size0_empty<_Tp>)
|
|
return _Size{}(__t) == 0;
|
|
else
|
|
return bool(_Begin{}(__t) == _End{}(__t));
|
|
}
|
|
};
|
|
|
|
template<typename _Tp>
|
|
concept __pointer_to_object = is_pointer_v<_Tp>
|
|
&& is_object_v<remove_pointer_t<_Tp>>;
|
|
|
|
template<typename _Tp>
|
|
concept __member_data = requires(_Tp& __t)
|
|
{
|
|
{ __cust_access::__decay_copy(__t.data()) } -> __pointer_to_object;
|
|
};
|
|
|
|
template<typename _Tp>
|
|
concept __begin_data = requires(_Tp& __t)
|
|
{ { _Begin{}(__t) } -> contiguous_iterator; };
|
|
|
|
struct _Data
|
|
{
|
|
private:
|
|
template<typename _Tp>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (__member_data<_Tp>)
|
|
return noexcept(__decay_copy(std::declval<_Tp&>().data()));
|
|
else
|
|
return noexcept(_Begin{}(std::declval<_Tp&>()));
|
|
}
|
|
|
|
public:
|
|
template<__maybe_borrowed_range _Tp>
|
|
requires __member_data<_Tp> || __begin_data<_Tp>
|
|
constexpr auto
|
|
operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>())
|
|
{
|
|
if constexpr (__member_data<_Tp>)
|
|
return __t.data();
|
|
else
|
|
return std::to_address(_Begin{}(__t));
|
|
}
|
|
};
|
|
|
|
struct _CData
|
|
{
|
|
template<typename _Tp>
|
|
constexpr auto
|
|
operator()(_Tp&& __e) const
|
|
noexcept(noexcept(_Data{}(__cust_access::__as_const<_Tp>(__e))))
|
|
requires requires { _Data{}(__cust_access::__as_const<_Tp>(__e)); }
|
|
{
|
|
return _Data{}(__cust_access::__as_const<_Tp>(__e));
|
|
}
|
|
};
|
|
|
|
} // namespace __cust_access
|
|
|
|
inline namespace __cust
|
|
{
|
|
inline constexpr __cust_access::_Begin begin{};
|
|
inline constexpr __cust_access::_End end{};
|
|
inline constexpr __cust_access::_CBegin cbegin{};
|
|
inline constexpr __cust_access::_CEnd cend{};
|
|
inline constexpr __cust_access::_RBegin rbegin{};
|
|
inline constexpr __cust_access::_REnd rend{};
|
|
inline constexpr __cust_access::_CRBegin crbegin{};
|
|
inline constexpr __cust_access::_CREnd crend{};
|
|
inline constexpr __cust_access::_Size size{};
|
|
inline constexpr __cust_access::_SSize ssize{};
|
|
inline constexpr __cust_access::_Empty empty{};
|
|
inline constexpr __cust_access::_Data data{};
|
|
inline constexpr __cust_access::_CData cdata{};
|
|
}
|
|
|
|
/// [range.range] The range concept.
|
|
template<typename _Tp>
|
|
concept range = requires(_Tp& __t)
|
|
{
|
|
ranges::begin(__t);
|
|
ranges::end(__t);
|
|
};
|
|
|
|
/// [range.range] The borrowed_range concept.
|
|
template<typename _Tp>
|
|
concept borrowed_range
|
|
= range<_Tp> && __detail::__maybe_borrowed_range<_Tp>;
|
|
|
|
template<typename _Tp>
|
|
using iterator_t = std::__detail::__range_iter_t<_Tp>;
|
|
|
|
template<range _Range>
|
|
using sentinel_t = decltype(ranges::end(std::declval<_Range&>()));
|
|
|
|
template<range _Range>
|
|
using range_difference_t = iter_difference_t<iterator_t<_Range>>;
|
|
|
|
template<range _Range>
|
|
using range_value_t = iter_value_t<iterator_t<_Range>>;
|
|
|
|
template<range _Range>
|
|
using range_reference_t = iter_reference_t<iterator_t<_Range>>;
|
|
|
|
template<range _Range>
|
|
using range_rvalue_reference_t
|
|
= iter_rvalue_reference_t<iterator_t<_Range>>;
|
|
|
|
/// [range.sized] The sized_range concept.
|
|
template<typename _Tp>
|
|
concept sized_range = range<_Tp>
|
|
&& requires(_Tp& __t) { ranges::size(__t); };
|
|
|
|
template<sized_range _Range>
|
|
using range_size_t = decltype(ranges::size(std::declval<_Range&>()));
|
|
|
|
/// [range.view] The ranges::view_base type.
|
|
struct view_base { };
|
|
|
|
/// [range.view] The ranges::enable_view boolean.
|
|
template<typename _Tp>
|
|
inline constexpr bool enable_view = derived_from<_Tp, view_base>;
|
|
|
|
/// [range.view] The ranges::view concept.
|
|
template<typename _Tp>
|
|
concept view
|
|
= range<_Tp> && movable<_Tp> && default_initializable<_Tp>
|
|
&& enable_view<_Tp>;
|
|
|
|
// [range.refinements]
|
|
|
|
/// A range for which ranges::begin returns an output iterator.
|
|
template<typename _Range, typename _Tp>
|
|
concept output_range
|
|
= range<_Range> && output_iterator<iterator_t<_Range>, _Tp>;
|
|
|
|
/// A range for which ranges::begin returns an input iterator.
|
|
template<typename _Tp>
|
|
concept input_range = range<_Tp> && input_iterator<iterator_t<_Tp>>;
|
|
|
|
/// A range for which ranges::begin returns a forward iterator.
|
|
template<typename _Tp>
|
|
concept forward_range
|
|
= input_range<_Tp> && forward_iterator<iterator_t<_Tp>>;
|
|
|
|
/// A range for which ranges::begin returns a bidirectional iterator.
|
|
template<typename _Tp>
|
|
concept bidirectional_range
|
|
= forward_range<_Tp> && bidirectional_iterator<iterator_t<_Tp>>;
|
|
|
|
/// A range for which ranges::begin returns a random access iterator.
|
|
template<typename _Tp>
|
|
concept random_access_range
|
|
= bidirectional_range<_Tp> && random_access_iterator<iterator_t<_Tp>>;
|
|
|
|
/// A range for which ranges::begin returns a contiguous iterator.
|
|
template<typename _Tp>
|
|
concept contiguous_range
|
|
= random_access_range<_Tp> && contiguous_iterator<iterator_t<_Tp>>
|
|
&& requires(_Tp& __t)
|
|
{
|
|
{ ranges::data(__t) } -> same_as<add_pointer_t<range_reference_t<_Tp>>>;
|
|
};
|
|
|
|
/// A range for which ranges::begin and ranges::end return the same type.
|
|
template<typename _Tp>
|
|
concept common_range
|
|
= range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>;
|
|
|
|
/// A range which can be safely converted to a view.
|
|
template<typename _Tp>
|
|
concept viewable_range = range<_Tp>
|
|
&& (borrowed_range<_Tp> || view<remove_cvref_t<_Tp>>);
|
|
|
|
// [range.iter.ops] range iterator operations
|
|
|
|
struct __advance_fn
|
|
{
|
|
template<input_or_output_iterator _It>
|
|
constexpr void
|
|
operator()(_It& __it, iter_difference_t<_It> __n) const
|
|
{
|
|
if constexpr (random_access_iterator<_It>)
|
|
__it += __n;
|
|
else if constexpr (bidirectional_iterator<_It>)
|
|
{
|
|
if (__n > 0)
|
|
{
|
|
do
|
|
{
|
|
++__it;
|
|
}
|
|
while (--__n);
|
|
}
|
|
else if (__n < 0)
|
|
{
|
|
do
|
|
{
|
|
--__it;
|
|
}
|
|
while (++__n);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// cannot decrement a non-bidirectional iterator
|
|
__glibcxx_assert(__n >= 0);
|
|
while (__n-- > 0)
|
|
++__it;
|
|
}
|
|
}
|
|
|
|
template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
|
|
constexpr void
|
|
operator()(_It& __it, _Sent __bound) const
|
|
{
|
|
if constexpr (assignable_from<_It&, _Sent>)
|
|
__it = std::move(__bound);
|
|
else if constexpr (sized_sentinel_for<_Sent, _It>)
|
|
(*this)(__it, __bound - __it);
|
|
else
|
|
{
|
|
while (__it != __bound)
|
|
++__it;
|
|
}
|
|
}
|
|
|
|
template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
|
|
constexpr iter_difference_t<_It>
|
|
operator()(_It& __it, iter_difference_t<_It> __n, _Sent __bound) const
|
|
{
|
|
if constexpr (sized_sentinel_for<_Sent, _It>)
|
|
{
|
|
const auto __diff = __bound - __it;
|
|
|
|
// n and bound must not lead in opposite directions:
|
|
__glibcxx_assert(__n == 0 || __diff == 0 || (__n < 0 == __diff < 0));
|
|
const auto __absdiff = __diff < 0 ? -__diff : __diff;
|
|
const auto __absn = __n < 0 ? -__n : __n;;
|
|
if (__absn >= __absdiff)
|
|
{
|
|
(*this)(__it, __bound);
|
|
return __n - __diff;
|
|
}
|
|
else
|
|
{
|
|
(*this)(__it, __n);
|
|
return 0;
|
|
}
|
|
}
|
|
else if (__it == __bound || __n == 0)
|
|
return __n;
|
|
else if (__n > 0)
|
|
{
|
|
iter_difference_t<_It> __m = 0;
|
|
do
|
|
{
|
|
++__it;
|
|
++__m;
|
|
}
|
|
while (__m != __n && __it != __bound);
|
|
return __n - __m;
|
|
}
|
|
else if constexpr (bidirectional_iterator<_It> && same_as<_It, _Sent>)
|
|
{
|
|
iter_difference_t<_It> __m = 0;
|
|
do
|
|
{
|
|
--__it;
|
|
--__m;
|
|
}
|
|
while (__m != __n && __it != __bound);
|
|
return __n - __m;
|
|
}
|
|
else
|
|
{
|
|
// cannot decrement a non-bidirectional iterator
|
|
__glibcxx_assert(__n >= 0);
|
|
return __n;
|
|
}
|
|
}
|
|
};
|
|
|
|
inline constexpr __advance_fn advance{};
|
|
|
|
struct __distance_fn
|
|
{
|
|
template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
|
|
constexpr iter_difference_t<_It>
|
|
operator()(_It __first, _Sent __last) const
|
|
{
|
|
if constexpr (sized_sentinel_for<_Sent, _It>)
|
|
return __last - __first;
|
|
else
|
|
{
|
|
iter_difference_t<_It> __n = 0;
|
|
while (__first != __last)
|
|
{
|
|
++__first;
|
|
++__n;
|
|
}
|
|
return __n;
|
|
}
|
|
}
|
|
|
|
template<range _Range>
|
|
constexpr range_difference_t<_Range>
|
|
operator()(_Range&& __r) const
|
|
{
|
|
if constexpr (sized_range<_Range>)
|
|
return static_cast<range_difference_t<_Range>>(ranges::size(__r));
|
|
else
|
|
return (*this)(ranges::begin(__r), ranges::end(__r));
|
|
}
|
|
};
|
|
|
|
inline constexpr __distance_fn distance{};
|
|
|
|
struct __next_fn
|
|
{
|
|
template<input_or_output_iterator _It>
|
|
constexpr _It
|
|
operator()(_It __x) const
|
|
{
|
|
++__x;
|
|
return __x;
|
|
}
|
|
|
|
template<input_or_output_iterator _It>
|
|
constexpr _It
|
|
operator()(_It __x, iter_difference_t<_It> __n) const
|
|
{
|
|
ranges::advance(__x, __n);
|
|
return __x;
|
|
}
|
|
|
|
template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
|
|
constexpr _It
|
|
operator()(_It __x, _Sent __bound) const
|
|
{
|
|
ranges::advance(__x, __bound);
|
|
return __x;
|
|
}
|
|
|
|
template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
|
|
constexpr _It
|
|
operator()(_It __x, iter_difference_t<_It> __n, _Sent __bound) const
|
|
{
|
|
ranges::advance(__x, __n, __bound);
|
|
return __x;
|
|
}
|
|
};
|
|
|
|
inline constexpr __next_fn next{};
|
|
|
|
struct __prev_fn
|
|
{
|
|
template<bidirectional_iterator _It>
|
|
constexpr _It
|
|
operator()(_It __x) const
|
|
{
|
|
--__x;
|
|
return __x;
|
|
}
|
|
|
|
template<bidirectional_iterator _It>
|
|
constexpr _It
|
|
operator()(_It __x, iter_difference_t<_It> __n) const
|
|
{
|
|
ranges::advance(__x, -__n);
|
|
return __x;
|
|
}
|
|
|
|
template<bidirectional_iterator _It>
|
|
constexpr _It
|
|
operator()(_It __x, iter_difference_t<_It> __n, _It __bound) const
|
|
{
|
|
ranges::advance(__x, -__n, __bound);
|
|
return __x;
|
|
}
|
|
};
|
|
|
|
inline constexpr __prev_fn prev{};
|
|
|
|
/// Type returned by algorithms instead of a dangling iterator or subrange.
|
|
struct dangling
|
|
{
|
|
constexpr dangling() noexcept = default;
|
|
template<typename... _Args>
|
|
constexpr dangling(_Args&&...) noexcept { }
|
|
};
|
|
|
|
template<range _Range>
|
|
using borrowed_iterator_t = conditional_t<borrowed_range<_Range>,
|
|
iterator_t<_Range>,
|
|
dangling>;
|
|
|
|
} // namespace ranges
|
|
_GLIBCXX_END_NAMESPACE_VERSION
|
|
} // namespace std
|
|
#endif // library concepts
|
|
#endif // C++20
|
|
#endif // _GLIBCXX_RANGES_BASE_H
|