Message ID | CANHA4Ojd8np1GfESRwtWDsS3XxcFe8c=1EMVPr4X+eiJW-zPYA@mail.gmail.com |
---|---|
State | New |
Headers | show |
Series | [RFC] span-v2: using concepts, constraints and ranges:: instead of enable_if | expand |
On 21/11/19 13:33 -0500, JeanHeyd Meneide wrote: >This is an attempt to use concepts and constraints rather than >ranges::. It builds and run the tests I ran on x86_64 Linux decently >(albeit I'm still new at doing this). I wanted to get a feel for >whether or not this had been done right: Thanks for doing this. >Thoughts? Needs moar changelog. >diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am >index 6300de9e96d..7f7035ca64e 100644 >--- a/libstdc++-v3/include/Makefile.am >+++ b/libstdc++-v3/include/Makefile.am >@@ -156,6 +156,7 @@ bits_headers = \ > ${bits_srcdir}/random.tcc \ > ${bits_srcdir}/range_access.h \ > ${bits_srcdir}/range_cmp.h \ >+ ${bits_srcdir}/range_concepts.h \ You've added this here, but not actually added a new header in the patch. >index 3843ba5d57f..c4a4e92fed0 100644 >--- a/libstdc++-v3/include/bits/iterator_concepts.h >+++ b/libstdc++-v3/include/bits/iterator_concepts.h >@@ -71,6 +71,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > { *__t } -> __can_reference; > }; > >+ template<typename _LeftType, typename _RightType> >+ concept __is_compatible_pointer = is_convertible<_RightType(*)[], _LeftType(*)[]>::value; This could use is_convertible_v. "left type" and "right type" don't mean much to me, I'd prefer to keep it conventional and _Tp/_Up or something meaninful like _From/_To. I'd prefer not to have this at namespace-scope at all, at least not unless we gie it a more descriptive name than __is_compatible_pointer. How are they compatible? What is the context? Something like array-compatible would be more descriptive, since that's the condition it's testing, and the context it's used in. std::unique_ptr<T[]> has similar checks so could reuse this ... except we can't use concepts in std::unique_ptr. For that reason, I think an alias template would be better, so it can be used in span and unique_ptr<T[]> e.g. template<typename _Tp, typename _Up> using __array_compatible = is_convertible<_Tp(*)[], _Up(*)[]>; That could be used in unique_ptr and can be used in span's constraints too. >+ > // FIXME: needed due to PR c++/67704 > template<__detail::__dereferenceable _Tp> > struct __iter_ref >diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h >index de074460c16..66697ffc354 100644 >--- a/libstdc++-v3/include/bits/range_access.h >+++ b/libstdc++-v3/include/bits/range_access.h >@@ -34,6 +34,7 @@ > > #if __cplusplus >= 201103L > #include <initializer_list> >+#include <concepts> This is redundant, it's included by the next header anyway ... > #include <bits/iterator_concepts.h> > > namespace std _GLIBCXX_VISIBILITY(default) >@@ -336,19 +337,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > ssize(const _Tp (&)[_Num]) noexcept > { return _Num; } > >- // "why are these in namespace std:: and not __gnu_cxx:: ?" >- // because if we don't put them here it's impossible to >- // have implicit ADL with "using std::begin/end/size/data;". >- template <typename _Container> >- constexpr auto >- __adl_begin(_Container& __cont) noexcept(noexcept(begin(__cont))) >- { return begin(__cont); } >- >- template <typename _Container> >- constexpr auto >- __adl_data(_Container& __cont) noexcept(noexcept(data(__cont))) >- { return data(__cont); } >- > #ifdef __cpp_lib_concepts > namespace ranges > { >@@ -869,10 +857,68 @@ namespace ranges > template<typename _Tp> > concept range = __detail::__range_impl<_Tp&>; > >+ template<range _Range> >+ using sentinel_t = decltype(ranges::end(std::declval<_Range&>())); >+ >+ template<range _Range> >+ using iterator_t = decltype(ranges::begin(std::declval<_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>>; >+ >+ template<range _Range> >+ using range_difference_t = iter_difference_t<iterator_t<_Range>>; >+ >+ namespace __detail >+ { >+ template<typename _Tp> >+ concept __forwarding_range = range<_Tp> && __range_impl<_Tp>; >+ } // namespace __detail >+ > /// [range.sized] The sized_range concept. > template<typename _Tp> > concept sized_range = range<_Tp> >- && requires(_Tp& __t) { ranges::size(__t); }; >+ && requires(_Tp& __t) { ranges::size(__t); };template<typename> >+ inline constexpr bool disable_sized_range = false; >+ >+ // [range.refinements] >+ template<typename _Range, typename _Tp> >+ concept output_range >+ = range<_Range> && output_iterator<iterator_t<_Range>, _Tp>; >+ >+ template<typename _Tp> >+ concept input_range = range<_Tp> && input_iterator<iterator_t<_Tp>>; >+ >+ template<typename _Tp> >+ concept forward_range >+ = input_range<_Tp> && forward_iterator<iterator_t<_Tp>>; >+ >+ template<typename _Tp> >+ concept bidirectional_range >+ = forward_range<_Tp> && bidirectional_iterator<iterator_t<_Tp>>; >+ >+ template<typename _Tp> >+ concept random_access_range >+ = bidirectional_range<_Tp> && random_access_iterator<iterator_t<_Tp>>; >+ >+ 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>>>; >+ }; >+ >+ template<typename _Tp> >+ concept common_range >+ = range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>; > > // [range.iter.ops] range iterator operations > >@@ -1008,12 +1054,6 @@ namespace ranges > } > } > >- template<range _Range> >- using iterator_t = decltype(ranges::begin(std::declval<_Range&>())); >- >- template<range _Range> >- using range_difference_t = iter_difference_t<iterator_t<_Range>>; >- > template<range _Range> > constexpr range_difference_t<_Range> > distance(_Range&& __r) >diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges >index 333d110b67e..a8b27764419 100644 >--- a/libstdc++-v3/include/std/ranges >+++ b/libstdc++-v3/include/std/ranges >@@ -43,6 +43,7 @@ > #include <iterator> > #include <limits> > #include <optional> >+#include <bits/range_concepts.h> Here's that new header again, which isn't in your patch. > > /** > * @defgroup ranges Ranges >@@ -55,66 +56,6 @@ namespace std _GLIBCXX_VISIBILITY(default) > _GLIBCXX_BEGIN_NAMESPACE_VERSION > namespace ranges > { >- // [range.range] The range concept. >- // Defined in <bits/range_iterator.h> >- // template<typename> concept range; Please leave this comment (although I have just realised it gets the name wrong ... oops). >diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span >index fcec22a6c57..09864dc8e13 100644 >--- a/libstdc++-v3/include/std/span >+++ b/libstdc++-v3/include/std/span >@@ -42,6 +42,7 @@ > #include <tuple> > #include <utility> > #include <array> >+#include <concepts> Redundant. > #include <bits/stl_iterator.h> > #include <bits/range_access.h> > >@@ -105,6 +106,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > size_t _M_extent_value; > }; > >+ // _GLIBCXX_RESOLVE_LIB_DEFECTS >+ // 3255. span's array constructor is too strict >+ template<typename _Type, size_t _Extent, typename _Tp, size_t _ArrayExtent> >+ concept __is_compatible_array >+ = (_Extent == dynamic_extent || _ArrayExtent == _Extent) >+ && __is_compatible_pointer<_Type, _Tp>; >+ >+ template<typename _Type, typename _Iter> >+ concept __is_compatible_iterator >+ = contiguous_iterator<_Iter> >+ && is_lvalue_reference_v<iter_reference_t<_Iter>> >+ && same_as<iter_value_t<_Iter>, remove_cvref_t<iter_reference_t<_Iter>>> >+ && __is_compatible_pointer<_Type, remove_reference_t<iter_reference_t<_Iter>>>; >+ >+ template<typename _Type, typename _Range> >+ concept __is_compatible_range >+ = __is_compatible_iterator<_Type, ranges::iterator_t<_Range>>; > } // namespace __detail > > template<typename _Type, size_t _Extent = dynamic_extent> >@@ -122,21 +140,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return dynamic_extent; > } > >- template<typename _Tp> >- using __is_compatible = is_convertible<_Tp(*)[], _Type(*)[]>; >- >- // _GLIBCXX_RESOLVE_LIB_DEFECTS >- // 3255. span's array constructor is too strict >- template<typename _Tp, size_t _ArrayExtent, >- typename = enable_if_t<_Extent == dynamic_extent >- || _ArrayExtent == _Extent>> >- using __is_compatible_array = __is_compatible<_Tp>; The advantage of defining these here, not at namespace-scope, is that you don't need to repeat the class template's parameters here. >- > public: > // member types > using value_type = remove_cv_t<_Type>; > using element_type = _Type; >- using index_type = size_t; >+ using size_type = size_t; > using reference = element_type&; > using const_reference = const element_type&; > using pointer = _Type*; >@@ -156,160 +164,74 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > // constructors > >- template<bool _DefaultConstructible = (_Extent + 1u) <= 1u, >- enable_if_t<_DefaultConstructible>* = nullptr> >- constexpr >- span() noexcept : _M_extent(0), _M_ptr(nullptr) >- { } >+ constexpr >+ span() noexcept >+ requires ((_Extent + 1u) <= 1u) >+ : _M_extent(0), _M_ptr(nullptr) >+ { } > > constexpr > span(const span&) noexcept = default; > >- template<typename _Tp, size_t _ArrayExtent, >- typename = _Require<__is_compatible_array<_Tp, _ArrayExtent>>> >+ template<typename _Tp, size_t _ArrayExtent> >+ requires __detail::__is_compatible_array<_Type, _Extent, _Tp, _ArrayExtent> > constexpr > span(_Tp (&__arr)[_ArrayExtent]) noexcept > : span(static_cast<pointer>(__arr), _ArrayExtent) > { } > >- template<typename _Tp, size_t _ArrayExtent, >- typename = _Require<__is_compatible_array<_Tp, _ArrayExtent>>> >+ template<typename _Tp, size_t _ArrayExtent> >+ requires __detail::__is_compatible_array<_Type, _Extent, _Tp, _ArrayExtent> You only need four template arguments here because you defined it at namespace-scope as a concept. What was wrong with the alias template? You could have left the alias template and used it like: requires __is_compatible_array<_Tp, _ArrayExtent>::value > constexpr > span(array<_Tp, _ArrayExtent>& __arr) noexcept > : span(static_cast<pointer>(__arr.data()), _ArrayExtent) > { } > >- template<typename _Tp, size_t _ArrayExtent, >- typename = _Require<__is_compatible_array<_Tp, _ArrayExtent>>> >+ template<typename _Tp, size_t _ArrayExtent> >+ requires __detail::__is_compatible_array<_Type, _Extent, const _Tp, _ArrayExtent> > constexpr > span(const array<_Tp, _ArrayExtent>& __arr) noexcept > : span(static_cast<pointer>(__arr.data()), _ArrayExtent) > { } > >- // NOTE: when the time comes, and P1394 - >- // range constructors for std::span - ships in >- // the standard, delete the #else block and remove >- // the conditional >- // if the paper fails, delete #if block >- // and keep the crappy #else block >- // and then cry that NB comments failed C++20... >- // but maybe for C++23? >-#ifdef _GLIBCXX_P1394 >- private: >- // FIXME: use std::iter_reference_t >- template<typename _Iterator> >- using iter_reference_t = decltype(*std::declval<_Iterator&>()); >- // FIXME: use std::ranges::iterator_t >- // N.B. constraint is needed to prevent a cycle when __adl_begin finds >- // begin(span) which does overload resolution on span(Range&&). >- template<typename _Rng, >- typename _Rng2 = remove_cvref_t<_Rng>, >- typename = enable_if_t<!__detail::__is_std_span<_Rng2>::value>> >- using iterator_t = decltype(std::__adl_begin(std::declval<_Rng&>())); >- // FIXME: use std::iter_value_t >- template<typename _Iter> >- using iter_value_t = typename iterator_traits<_Iter>::value_type; >- // FIXME: use std::derived_from concept >- template<typename _Derived, typename _Base> >- using derived_from >- = __and_<is_base_of<_Base, _Derived>, >- is_convertible<const volatile _Derived*, const volatile _Base*>>; >- // FIXME: require contiguous_iterator<_Iterator> >- template<typename _Iter, >- typename _Ref = iter_reference_t<_Iter>, >- typename _Traits = iterator_traits<_Iter>, >- typename _Tag = typename _Traits::iterator_category> >- using __is_compatible_iterator >- = __and_<derived_from<_Tag, random_access_iterator_tag>, >- is_lvalue_reference<_Ref>, >- is_same<iter_value_t<_Iter>, remove_cvref_t<_Ref>>, >- __is_compatible<remove_reference_t<_Ref>>>; >- >- template<typename _Range> >- using __is_compatible_range >- = __is_compatible_iterator<iterator_t<_Range>>; >- > public: >- template<typename _Range, typename = _Require< >- bool_constant<_Extent == dynamic_extent>, >- __not_<__detail::__is_std_span<remove_cvref_t<_Range>>>, >- __not_<__detail::__is_std_array<remove_cvref_t<_Range>>>, >- __not_<is_array<remove_reference_t<_Range>>>, >- __is_compatible_range<_Range>>, >- typename = decltype(std::__adl_data(std::declval<_Range&>()))> >+ template<ranges::contiguous_range _Range> >+ requires (_Extent == dynamic_extent) >+ && (!__detail::__is_std_span<remove_cvref_t<_Range>>::value) >+ && (!__detail::__is_std_array<remove_cvref_t<_Range>>::value) >+ && (!is_array<remove_reference_t<_Range>>::value) is_array_v<...> instead of is_array<...>::value >+ && __detail::__is_compatible_range<_Type, _Range> > constexpr > span(_Range&& __range) >- noexcept(noexcept(::std::__adl_data(__range)) >- && noexcept(::std::__adl_size(__range))) >- : span(::std::__adl_data(__range), ::std::__adl_size(__range)) >+ noexcept(noexcept(ranges::data(__range)) >+ && noexcept(ranges::size(__range))) >+ : span(ranges::data(__range), ranges::size(__range)) > { } > >- template<typename _ContiguousIterator, typename _Sentinel, typename >- = _Require<__not_<is_convertible<_Sentinel, index_type>>, >- __is_compatible_iterator<_ContiguousIterator>>> >+ template<contiguous_iterator _ContiguousIterator, >+ sized_sentinel_for<_ContiguousIterator> _Sentinel> >+ requires __detail::__is_compatible_iterator<_Type, _ContiguousIterator> >+ && (!is_convertible<_Sentinel, size_type>::value) is_convertible_v
Thank you for the review! - I moved the concepts back into the class and changed them to template aliases because class local concepts aren't a thing that works. + They are not inline constexpr bools because that ICE's GCC pretty hard, as you explained to me earlier, so we use ::value in some places rather than _v - I named the alias __is_array_convertible, to match what the intent is, and named the template parameters very explicitly. + I moved it into type_traits, but I didn't change bits/unique_ptr.h or shared_ptr yet because I'm not confident I can do that quickly in one go. + (And it should probably be a different patch anyways, but it should be in the right place when we introduce it here.) --------- 2019-11-21 JeanHeyd "ThePhD" Meneide <phdofthehouse@gmail.com> * include/std/span: Use concepts and ranges:: calls instead of enable_if and friends. * include/std/ranges: tear basic concepts out and put in range_access.h for "ranges lite" internal header. * include/std/type_traits: __is_array_convertible trait. Is common to unique/shared_ptr as well (preparation for changing unique/shared_ptr soon). * include/bits/range_access.h: Remove __adl_* versions of access functions. diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h index de074460c16..c0891c2f235 100644 --- a/libstdc++-v3/include/bits/range_access.h +++ b/libstdc++-v3/include/bits/range_access.h @@ -336,19 +336,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ssize(const _Tp (&)[_Num]) noexcept { return _Num; } - // "why are these in namespace std:: and not __gnu_cxx:: ?" - // because if we don't put them here it's impossible to - // have implicit ADL with "using std::begin/end/size/data;". - template <typename _Container> - constexpr auto - __adl_begin(_Container& __cont) noexcept(noexcept(begin(__cont))) - { return begin(__cont); } - - template <typename _Container> - constexpr auto - __adl_data(_Container& __cont) noexcept(noexcept(data(__cont))) - { return data(__cont); } - #ifdef __cpp_lib_concepts namespace ranges { @@ -869,10 +856,70 @@ namespace ranges template<typename _Tp> concept range = __detail::__range_impl<_Tp&>; + template<range _Range> + using sentinel_t = decltype(ranges::end(std::declval<_Range&>())); + + template<range _Range> + using iterator_t = decltype(ranges::begin(std::declval<_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>>; + + template<range _Range> + using range_difference_t = iter_difference_t<iterator_t<_Range>>; + + namespace __detail + { + template<typename _Tp> + concept __forwarding_range = range<_Tp> && __range_impl<_Tp>; + } // namespace __detail + /// [range.sized] The sized_range concept. template<typename _Tp> concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); }; + + template<typename> + inline constexpr bool disable_sized_range = false; + + // [range.refinements] + template<typename _Range, typename _Tp> + concept output_range + = range<_Range> && output_iterator<iterator_t<_Range>, _Tp>; + + template<typename _Tp> + concept input_range = range<_Tp> && input_iterator<iterator_t<_Tp>>; + + template<typename _Tp> + concept forward_range + = input_range<_Tp> && forward_iterator<iterator_t<_Tp>>; + + template<typename _Tp> + concept bidirectional_range + = forward_range<_Tp> && bidirectional_iterator<iterator_t<_Tp>>; + + template<typename _Tp> + concept random_access_range + = bidirectional_range<_Tp> && random_access_iterator<iterator_t<_Tp>>; + + 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>>>; + }; + + template<typename _Tp> + concept common_range + = range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>; // [range.iter.ops] range iterator operations @@ -1008,12 +1055,6 @@ namespace ranges } } - template<range _Range> - using iterator_t = decltype(ranges::begin(std::declval<_Range&>())); - - template<range _Range> - using range_difference_t = iter_difference_t<iterator_t<_Range>>; - template<range _Range> constexpr range_difference_t<_Range> distance(_Range&& __r) diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 333d110b67e..e1bf6eec5d0 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -56,64 +56,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION namespace ranges { // [range.range] The range concept. - // Defined in <bits/range_iterator.h> - // template<typename> concept range; - - template<range _Range> - using sentinel_t = decltype(ranges::end(std::declval<_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>>; - - namespace __detail - { - template<typename _Tp> - concept __forwarding_range = range<_Tp> && __range_impl<_Tp>; - } // namespace __detail - // [range.sized] The sized_range concept. - // Defined in <bits/range_iterator.h> - // template<typename> concept sized_range; + // Defined in <bits/range_access.h> // [range.refinements] - - template<typename _Range, typename _Tp> - concept output_range - = range<_Range> && output_iterator<iterator_t<_Range>, _Tp>; - - template<typename _Tp> - concept input_range = range<_Tp> && input_iterator<iterator_t<_Tp>>; - - template<typename _Tp> - concept forward_range - = input_range<_Tp> && forward_iterator<iterator_t<_Tp>>; - - template<typename _Tp> - concept bidirectional_range - = forward_range<_Tp> && bidirectional_iterator<iterator_t<_Tp>>; - - template<typename _Tp> - concept random_access_range - = bidirectional_range<_Tp> && random_access_iterator<iterator_t<_Tp>>; - - 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>>>; - }; - - template<typename _Tp> - concept common_range - = range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>; + // Defined in <bits/range_access.h> struct view_base { }; diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span index fcec22a6c57..2717645c406 100644 --- a/libstdc++-v3/include/std/span +++ b/libstdc++-v3/include/std/span @@ -104,7 +104,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: size_t _M_extent_value; }; - } // namespace __detail template<typename _Type, size_t _Extent = dynamic_extent> @@ -122,21 +121,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return dynamic_extent; } - template<typename _Tp> - using __is_compatible = is_convertible<_Tp(*)[], _Type(*)[]>; - // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3255. span's array constructor is too strict - template<typename _Tp, size_t _ArrayExtent, - typename = enable_if_t<_Extent == dynamic_extent - || _ArrayExtent == _Extent>> - using __is_compatible_array = __is_compatible<_Tp>; + template<typename _Tp, size_t _ArrayExtent> + using __is_compatible_array = __and_< + bool_constant<(_Extent == dynamic_extent || _ArrayExtent == _Extent)>, + __is_array_convertible<_Type, _Tp>>; + + template<typename _Iter> + using __is_compatible_iterator = __and_< + bool_constant<contiguous_iterator<_Iter>>, + is_lvalue_reference<iter_reference_t<_Iter>>, + is_same<iter_value_t<_Iter>, remove_cvref_t<iter_reference_t<_Iter>>>, + __is_array_convertible<_Type, remove_reference_t<iter_reference_t<_Iter>>>>; + + template<typename _Range> + using __is_compatible_range + = __is_compatible_iterator<ranges::iterator_t<_Range>>; public: // member types using value_type = remove_cv_t<_Type>; using element_type = _Type; - using index_type = size_t; + using size_type = size_t; using reference = element_type&; using const_reference = const element_type&; using pointer = _Type*; @@ -156,160 +163,74 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // constructors - template<bool _DefaultConstructible = (_Extent + 1u) <= 1u, - enable_if_t<_DefaultConstructible>* = nullptr> - constexpr - span() noexcept : _M_extent(0), _M_ptr(nullptr) - { } + constexpr + span() noexcept + requires ((_Extent + 1u) <= 1u) + : _M_extent(0), _M_ptr(nullptr) + { } constexpr span(const span&) noexcept = default; - template<typename _Tp, size_t _ArrayExtent, - typename = _Require<__is_compatible_array<_Tp, _ArrayExtent>>> + template<typename _Tp, size_t _ArrayExtent> + requires (__is_compatible_array<_Tp, _ArrayExtent>::value) constexpr span(_Tp (&__arr)[_ArrayExtent]) noexcept : span(static_cast<pointer>(__arr), _ArrayExtent) { } - template<typename _Tp, size_t _ArrayExtent, - typename = _Require<__is_compatible_array<_Tp, _ArrayExtent>>> + template<typename _Tp, size_t _ArrayExtent> + requires (__is_compatible_array<_Tp, _ArrayExtent>::value) constexpr span(array<_Tp, _ArrayExtent>& __arr) noexcept : span(static_cast<pointer>(__arr.data()), _ArrayExtent) { } - template<typename _Tp, size_t _ArrayExtent, - typename = _Require<__is_compatible_array<_Tp, _ArrayExtent>>> + template<typename _Tp, size_t _ArrayExtent> + requires (__is_compatible_array<const _Tp, _ArrayExtent>::value) constexpr span(const array<_Tp, _ArrayExtent>& __arr) noexcept : span(static_cast<pointer>(__arr.data()), _ArrayExtent) { } - // NOTE: when the time comes, and P1394 - - // range constructors for std::span - ships in - // the standard, delete the #else block and remove - // the conditional - // if the paper fails, delete #if block - // and keep the crappy #else block - // and then cry that NB comments failed C++20... - // but maybe for C++23? -#ifdef _GLIBCXX_P1394 - private: - // FIXME: use std::iter_reference_t - template<typename _Iterator> - using iter_reference_t = decltype(*std::declval<_Iterator&>()); - // FIXME: use std::ranges::iterator_t - // N.B. constraint is needed to prevent a cycle when __adl_begin finds - // begin(span) which does overload resolution on span(Range&&). - template<typename _Rng, - typename _Rng2 = remove_cvref_t<_Rng>, - typename = enable_if_t<!__detail::__is_std_span<_Rng2>::value>> - using iterator_t = decltype(std::__adl_begin(std::declval<_Rng&>())); - // FIXME: use std::iter_value_t - template<typename _Iter> - using iter_value_t = typename iterator_traits<_Iter>::value_type; - // FIXME: use std::derived_from concept - template<typename _Derived, typename _Base> - using derived_from - = __and_<is_base_of<_Base, _Derived>, - is_convertible<const volatile _Derived*, const volatile _Base*>>; - // FIXME: require contiguous_iterator<_Iterator> - template<typename _Iter, - typename _Ref = iter_reference_t<_Iter>, - typename _Traits = iterator_traits<_Iter>, - typename _Tag = typename _Traits::iterator_category> - using __is_compatible_iterator - = __and_<derived_from<_Tag, random_access_iterator_tag>, - is_lvalue_reference<_Ref>, - is_same<iter_value_t<_Iter>, remove_cvref_t<_Ref>>, - __is_compatible<remove_reference_t<_Ref>>>; - - template<typename _Range> - using __is_compatible_range - = __is_compatible_iterator<iterator_t<_Range>>; - public: - template<typename _Range, typename = _Require< - bool_constant<_Extent == dynamic_extent>, - __not_<__detail::__is_std_span<remove_cvref_t<_Range>>>, - __not_<__detail::__is_std_array<remove_cvref_t<_Range>>>, - __not_<is_array<remove_reference_t<_Range>>>, - __is_compatible_range<_Range>>, - typename = decltype(std::__adl_data(std::declval<_Range&>()))> + template<ranges::contiguous_range _Range> + requires (_Extent == dynamic_extent) + && (!__detail::__is_std_span<remove_cvref_t<_Range>>::value) + && (!__detail::__is_std_array<remove_cvref_t<_Range>>::value) + && (!is_array_v<remove_reference_t<_Range>>) + && (__is_compatible_range<_Range>::value) constexpr span(_Range&& __range) - noexcept(noexcept(::std::__adl_data(__range)) - && noexcept(::std::__adl_size(__range))) - : span(::std::__adl_data(__range), ::std::__adl_size(__range)) + noexcept(noexcept(ranges::data(__range)) + && noexcept(ranges::size(__range))) + : span(ranges::data(__range), ranges::size(__range)) { } - template<typename _ContiguousIterator, typename _Sentinel, typename - = _Require<__not_<is_convertible<_Sentinel, index_type>>, - __is_compatible_iterator<_ContiguousIterator>>> + template<contiguous_iterator _ContiguousIterator, + sized_sentinel_for<_ContiguousIterator> _Sentinel> + requires (__is_compatible_iterator<_ContiguousIterator>::value) + && (!is_convertible_v<_Sentinel, size_type>) constexpr span(_ContiguousIterator __first, _Sentinel __last) - : _M_extent(static_cast<index_type>(__last - __first)), + : _M_extent(static_cast<size_type>(__last - __first)), _M_ptr(std::to_address(__first)) { if (_Extent != dynamic_extent) __glibcxx_assert((__last - __first) == _Extent); } - template<typename _ContiguousIterator, typename - = _Require<__is_compatible_iterator<_ContiguousIterator>>> + template<contiguous_iterator _ContiguousIterator> + requires (__is_compatible_iterator<_ContiguousIterator>::value) constexpr - span(_ContiguousIterator __first, index_type __count) + span(_ContiguousIterator __first, size_type __count) noexcept(noexcept(std::to_address(__first))) : _M_extent(__count), _M_ptr(std::to_address(__first)) { __glibcxx_assert(_Extent == dynamic_extent || __count == _Extent); } -#else - private: - template<typename _Container, - typename _DataT = decltype(std::data(std::declval<_Container&>())), - typename _SizeT = decltype(std::size(std::declval<_Container&>()))> - using __is_compatible_container - = __is_compatible<remove_pointer_t<_DataT>>; - public: - template<typename _Container, typename = _Require< - bool_constant<_Extent == dynamic_extent>, - __not_<__detail::__is_std_span<remove_cv_t<_Container>>>, - __not_<__detail::__is_std_array<remove_cv_t<_Container>>>, - __not_<is_array<_Container>>, - __is_compatible_container<_Container>>> - constexpr - span(_Container& __cont) - noexcept(noexcept(std::data(__cont)) && noexcept(std::size(__cont))) - : _M_extent(std::size(__cont)), _M_ptr(std::data(__cont)) - { } - - template<typename _Container, typename = _Require< - bool_constant<_Extent == dynamic_extent>, - __not_<__detail::__is_std_span<remove_cv_t<_Container>>>, - __not_<__detail::__is_std_array<remove_cv_t<_Container>>>, - __not_<is_array<_Container>>, - __is_compatible_container<const _Container>>> - constexpr - span(const _Container& __cont) - noexcept(noexcept(std::data(__cont)) && noexcept(std::size(__cont))) - : _M_extent(std::size(__cont)), _M_ptr(std::data(__cont)) - { } - - constexpr - span(pointer __first, index_type __count) noexcept - : _M_extent(__count), _M_ptr(__first) - { __glibcxx_assert(_Extent == dynamic_extent || __count == _Extent); } - - constexpr - span(pointer __first, pointer __last) noexcept - : span(__first, static_cast<index_type>(__last - __first)) - { } -#endif // P1394 - - template<typename _OType, size_t _OExtent, typename = _Require< - __bool_constant<_Extent == dynamic_extent || _Extent == _OExtent>, - is_convertible<_OType(*)[], _Type(*)[]>>> + template<typename _OType, size_t _OExtent> + requires (_Extent == dynamic_extent || _Extent == _OExtent) + && (__is_array_convertible<_Type, _OType>::value) constexpr span(const span<_OType, _OExtent>& __s) noexcept : _M_extent(__s.size()), _M_ptr(__s.data()) @@ -322,11 +243,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // observers - constexpr index_type + constexpr size_type size() const noexcept { return this->_M_extent._M_extent(); } - constexpr index_type + constexpr size_type size_bytes() const noexcept { return this->_M_extent._M_extent() * sizeof(element_type); } @@ -353,7 +274,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } constexpr reference - operator[](index_type __idx) const noexcept + operator[](size_type __idx) const noexcept { static_assert(extent != 0); __glibcxx_assert(__idx < size()); @@ -412,7 +333,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } constexpr span<element_type, dynamic_extent> - first(index_type __count) const noexcept + first(size_type __count) const noexcept { __glibcxx_assert(__count <= size()); return { this->data(), __count }; @@ -430,7 +351,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } constexpr span<element_type, dynamic_extent> - last(index_type __count) const noexcept + last(size_type __count) const noexcept { __glibcxx_assert(__count <= size()); return { this->data() + (this->size() - __count), __count }; @@ -465,7 +386,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } constexpr span<element_type, dynamic_extent> - subspan(index_type __offset, index_type __count = dynamic_extent) const + subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept { __glibcxx_assert(__offset <= size()); @@ -505,27 +426,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION span(const array<_Type, _ArrayExtent>&) -> span<const _Type, _ArrayExtent>; -#ifdef _GLIBCXX_P1394 - - template<typename _ContiguousIterator, typename _Sentinel> + template<contiguous_iterator _ContiguousIterator, typename _Sentinel> span(_ContiguousIterator, _Sentinel) - -> span<remove_reference_t< - typename iterator_traits<_ContiguousIterator>::reference>>; + -> span<remove_reference_t<ranges::range_reference_t<_ContiguousIterator>>>; template<typename _Range> span(_Range &&) - -> span<remove_reference_t<typename iterator_traits< - decltype(std::__adl_begin(::std::declval<_Range&>()))>::reference>>; - -#else - - template<typename _Container> - span(_Container&) -> span<typename _Container::value_type>; - - template<typename _Container> - span(const _Container&) -> span<const typename _Container::value_type>; - -#endif // P1394 + -> span<remove_reference_t<ranges::range_reference_t<_Range&>>>; template<typename _Type, size_t _Extent> inline diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index 8e787a994c3..28981d84e76 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits @@ -1475,6 +1475,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; #pragma GCC diagnostic pop + // helper trait for unique_ptr<T[]>, shared_ptr<T[]>, and span<T, N> + template<typename _ToElementType, typename _FromElementType> + using __is_array_convertible + = is_convertible<_FromElementType(*)[], _ToElementType(*)[]>; + // is_nothrow_convertible for C++11 template<typename _From, typename _To> struct __is_nothrow_convertible
On 21/11/19 19:56 -0500, JeanHeyd Meneide wrote: >Thank you for the review! > >- I moved the concepts back into the class and changed them to >template aliases because class local concepts aren't a thing that >works. > + They are not inline constexpr bools because that ICE's GCC >pretty hard, as you explained to me earlier, so we use ::value in some >places rather than _v >- I named the alias __is_array_convertible, to match what the intent >is, and named the template parameters very explicitly. > + I moved it into type_traits, but I didn't change >bits/unique_ptr.h or shared_ptr yet because I'm not confident I can do >that quickly in one go. > + (And it should probably be a different patch anyways, but it >should be in the right place when we introduce it here.) >+ template<typename _Iter> >+ using __is_compatible_iterator = __and_< >+ bool_constant<contiguous_iterator<_Iter>>, >+ is_lvalue_reference<iter_reference_t<_Iter>>, >+ is_same<iter_value_t<_Iter>, remove_cvref_t<iter_reference_t<_Iter>>>, >+ __is_array_convertible<_Type, remove_reference_t<iter_reference_t<_Iter>>>>; This line is too long, we stick to < 80 columns. I'm changing it to: template<typename _Iter, typename _Ref = iter_reference_t<_Iter>> using __is_compatible_iterator = __and_< bool_constant<contiguous_iterator<_Iter>>, is_lvalue_reference<iter_reference_t<_Iter>>, is_same<iter_value_t<_Iter>, remove_cvref_t<_Ref>>, __is_array_convertible<_Type, remove_reference_t<_Ref>>>; > public: > // member types > using value_type = remove_cv_t<_Type>; > using element_type = _Type; >- using index_type = size_t; >+ using size_type = size_t; The comment about size_type was still there, so I'm removing that: - // Official wording has no size_type -- why?? - // using size_type = size_t; >@@ -505,27 +426,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > span(const array<_Type, _ArrayExtent>&) > -> span<const _Type, _ArrayExtent>; > >-#ifdef _GLIBCXX_P1394 >- >- template<typename _ContiguousIterator, typename _Sentinel> >+ template<contiguous_iterator _ContiguousIterator, typename _Sentinel> > span(_ContiguousIterator, _Sentinel) >- -> span<remove_reference_t< >- typename iterator_traits<_ContiguousIterator>::reference>>; >+ -> span<remove_reference_t<ranges::range_reference_t<_ContiguousIterator>>>; This line's too long, so I'm renaming _ContiguousIterator to _Iter. When it's a constrained parameter I think we can use less verbose names. There's no need to use the template parameter's name to express the (implied, unchecked) requirements on it when we constrain it with a concept. Since <span> is now using concepts throughout, I'm adding this around the whole thing: #if __cpp_lib_concepts This also causes two new FAILs in 23_containers/span/lwg3255.cc but that turns out to be a bug in the test, which fails now t hat you've fixed a bug in span. I'll fix that as well. With those changes I'm committing it to trunk, thanks! Tested powerpc64le-linux, committed to trunk as attached.
On 05/12/19 13:49 +0000, Jonathan Wakely wrote: >This also causes two new FAILs in 23_containers/span/lwg3255.cc but >that turns out to be a bug in the test, which fails now t hat you've >fixed a bug in span. I'll fix that as well. Here's that fix for the buggy test. Tested powerpc64le-linux, committed to trunk as attached.
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 6300de9e96d..7f7035ca64e 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -156,6 +156,7 @@ bits_headers = \ ${bits_srcdir}/random.tcc \ ${bits_srcdir}/range_access.h \ ${bits_srcdir}/range_cmp.h \ + ${bits_srcdir}/range_concepts.h \ ${bits_srcdir}/refwrap.h \ ${bits_srcdir}/regex.h \ ${bits_srcdir}/regex.tcc \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index ae4a493ea65..475106ee69b 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -500,6 +500,7 @@ bits_headers = \ ${bits_srcdir}/random.tcc \ ${bits_srcdir}/range_access.h \ ${bits_srcdir}/range_cmp.h \ + ${bits_srcdir}/range_concepts.h \ ${bits_srcdir}/refwrap.h \ ${bits_srcdir}/regex.h \ ${bits_srcdir}/regex.tcc \ diff --git a/libstdc++-v3/include/bits/iterator_concepts.h b/libstdc++-v3/include/bits/iterator_concepts.h index 3843ba5d57f..c4a4e92fed0 100644 --- a/libstdc++-v3/include/bits/iterator_concepts.h +++ b/libstdc++-v3/include/bits/iterator_concepts.h @@ -71,6 +71,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { *__t } -> __can_reference; }; + template<typename _LeftType, typename _RightType> + concept __is_compatible_pointer = is_convertible<_RightType(*)[], _LeftType(*)[]>::value; + // FIXME: needed due to PR c++/67704 template<__detail::__dereferenceable _Tp> struct __iter_ref diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h index de074460c16..66697ffc354 100644 --- a/libstdc++-v3/include/bits/range_access.h +++ b/libstdc++-v3/include/bits/range_access.h @@ -34,6 +34,7 @@ #if __cplusplus >= 201103L #include <initializer_list> +#include <concepts> #include <bits/iterator_concepts.h> namespace std _GLIBCXX_VISIBILITY(default) @@ -336,19 +337,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ssize(const _Tp (&)[_Num]) noexcept { return _Num; } - // "why are these in namespace std:: and not __gnu_cxx:: ?" - // because if we don't put them here it's impossible to - // have implicit ADL with "using std::begin/end/size/data;". - template <typename _Container> - constexpr auto - __adl_begin(_Container& __cont) noexcept(noexcept(begin(__cont))) - { return begin(__cont); } - - template <typename _Container> - constexpr auto - __adl_data(_Container& __cont) noexcept(noexcept(data(__cont))) - { return data(__cont); } - #ifdef __cpp_lib_concepts namespace ranges { @@ -869,10 +857,68 @@ namespace ranges template<typename _Tp> concept range = __detail::__range_impl<_Tp&>; + template<range _Range> + using sentinel_t = decltype(ranges::end(std::declval<_Range&>())); + + template<range _Range> + using iterator_t = decltype(ranges::begin(std::declval<_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>>; + + template<range _Range> + using range_difference_t = iter_difference_t<iterator_t<_Range>>; + + namespace __detail + { + template<typename _Tp> + concept __forwarding_range = range<_Tp> && __range_impl<_Tp>; + } // namespace __detail + /// [range.sized] The sized_range concept. template<typename _Tp> concept sized_range = range<_Tp> - && requires(_Tp& __t) { ranges::size(__t); }; + && requires(_Tp& __t) { ranges::size(__t); };template<typename> + inline constexpr bool disable_sized_range = false; + + // [range.refinements] + template<typename _Range, typename _Tp> + concept output_range + = range<_Range> && output_iterator<iterator_t<_Range>, _Tp>; + + template<typename _Tp> + concept input_range = range<_Tp> && input_iterator<iterator_t<_Tp>>; + + template<typename _Tp> + concept forward_range + = input_range<_Tp> && forward_iterator<iterator_t<_Tp>>; + + template<typename _Tp> + concept bidirectional_range + = forward_range<_Tp> && bidirectional_iterator<iterator_t<_Tp>>; + + template<typename _Tp> + concept random_access_range + = bidirectional_range<_Tp> && random_access_iterator<iterator_t<_Tp>>; + + 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>>>; + }; + + template<typename _Tp> + concept common_range + = range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>; // [range.iter.ops] range iterator operations @@ -1008,12 +1054,6 @@ namespace ranges } } - template<range _Range> - using iterator_t = decltype(ranges::begin(std::declval<_Range&>())); - - template<range _Range> - using range_difference_t = iter_difference_t<iterator_t<_Range>>; - template<range _Range> constexpr range_difference_t<_Range> distance(_Range&& __r) diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 333d110b67e..a8b27764419 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -43,6 +43,7 @@ #include <iterator> #include <limits> #include <optional> +#include <bits/range_concepts.h> /** * @defgroup ranges Ranges @@ -55,66 +56,6 @@ namespace std _GLIBCXX_VISIBILITY(default) _GLIBCXX_BEGIN_NAMESPACE_VERSION namespace ranges { - // [range.range] The range concept. - // Defined in <bits/range_iterator.h> - // template<typename> concept range; - - template<range _Range> - using sentinel_t = decltype(ranges::end(std::declval<_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>>; - - namespace __detail - { - template<typename _Tp> - concept __forwarding_range = range<_Tp> && __range_impl<_Tp>; - } // namespace __detail - - // [range.sized] The sized_range concept. - // Defined in <bits/range_iterator.h> - // template<typename> concept sized_range; - - // [range.refinements] - - template<typename _Range, typename _Tp> - concept output_range - = range<_Range> && output_iterator<iterator_t<_Range>, _Tp>; - - template<typename _Tp> - concept input_range = range<_Tp> && input_iterator<iterator_t<_Tp>>; - - template<typename _Tp> - concept forward_range - = input_range<_Tp> && forward_iterator<iterator_t<_Tp>>; - - template<typename _Tp> - concept bidirectional_range - = forward_range<_Tp> && bidirectional_iterator<iterator_t<_Tp>>; - - template<typename _Tp> - concept random_access_range - = bidirectional_range<_Tp> && random_access_iterator<iterator_t<_Tp>>; - - 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>>>; - }; - - template<typename _Tp> - concept common_range - = range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>; - struct view_base { }; namespace __detail diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span index fcec22a6c57..09864dc8e13 100644 --- a/libstdc++-v3/include/std/span +++ b/libstdc++-v3/include/std/span @@ -42,6 +42,7 @@ #include <tuple> #include <utility> #include <array> +#include <concepts> #include <bits/stl_iterator.h> #include <bits/range_access.h> @@ -105,6 +106,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION size_t _M_extent_value; }; + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3255. span's array constructor is too strict + template<typename _Type, size_t _Extent, typename _Tp, size_t _ArrayExtent> + concept __is_compatible_array + = (_Extent == dynamic_extent || _ArrayExtent == _Extent) + && __is_compatible_pointer<_Type, _Tp>; + + template<typename _Type, typename _Iter> + concept __is_compatible_iterator + = contiguous_iterator<_Iter> + && is_lvalue_reference_v<iter_reference_t<_Iter>> + && same_as<iter_value_t<_Iter>, remove_cvref_t<iter_reference_t<_Iter>>> + && __is_compatible_pointer<_Type, remove_reference_t<iter_reference_t<_Iter>>>; + + template<typename _Type, typename _Range> + concept __is_compatible_range + = __is_compatible_iterator<_Type, ranges::iterator_t<_Range>>; } // namespace __detail template<typename _Type, size_t _Extent = dynamic_extent> @@ -122,21 +140,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return dynamic_extent; } - template<typename _Tp> - using __is_compatible = is_convertible<_Tp(*)[], _Type(*)[]>; - - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 3255. span's array constructor is too strict - template<typename _Tp, size_t _ArrayExtent, - typename = enable_if_t<_Extent == dynamic_extent - || _ArrayExtent == _Extent>> - using __is_compatible_array = __is_compatible<_Tp>; - public: // member types using value_type = remove_cv_t<_Type>; using element_type = _Type; - using index_type = size_t; + using size_type = size_t; using reference = element_type&; using const_reference = const element_type&; using pointer = _Type*; @@ -156,160 +164,74 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // constructors - template<bool _DefaultConstructible = (_Extent + 1u) <= 1u, - enable_if_t<_DefaultConstructible>* = nullptr> - constexpr - span() noexcept : _M_extent(0), _M_ptr(nullptr) - { } + constexpr + span() noexcept + requires ((_Extent + 1u) <= 1u) + : _M_extent(0), _M_ptr(nullptr) + { } constexpr span(const span&) noexcept = default; - template<typename _Tp, size_t _ArrayExtent, - typename = _Require<__is_compatible_array<_Tp, _ArrayExtent>>> + template<typename _Tp, size_t _ArrayExtent> + requires __detail::__is_compatible_array<_Type, _Extent, _Tp, _ArrayExtent> constexpr span(_Tp (&__arr)[_ArrayExtent]) noexcept : span(static_cast<pointer>(__arr), _ArrayExtent) { } - template<typename _Tp, size_t _ArrayExtent, - typename = _Require<__is_compatible_array<_Tp, _ArrayExtent>>> + template<typename _Tp, size_t _ArrayExtent> + requires __detail::__is_compatible_array<_Type, _Extent, _Tp, _ArrayExtent> constexpr span(array<_Tp, _ArrayExtent>& __arr) noexcept : span(static_cast<pointer>(__arr.data()), _ArrayExtent) { } - template<typename _Tp, size_t _ArrayExtent, - typename = _Require<__is_compatible_array<_Tp, _ArrayExtent>>> + template<typename _Tp, size_t _ArrayExtent> + requires __detail::__is_compatible_array<_Type, _Extent, const _Tp, _ArrayExtent> constexpr span(const array<_Tp, _ArrayExtent>& __arr) noexcept : span(static_cast<pointer>(__arr.data()), _ArrayExtent) { } - // NOTE: when the time comes, and P1394 - - // range constructors for std::span - ships in - // the standard, delete the #else block and remove - // the conditional - // if the paper fails, delete #if block - // and keep the crappy #else block - // and then cry that NB comments failed C++20... - // but maybe for C++23? -#ifdef _GLIBCXX_P1394 - private: - // FIXME: use std::iter_reference_t - template<typename _Iterator> - using iter_reference_t = decltype(*std::declval<_Iterator&>()); - // FIXME: use std::ranges::iterator_t - // N.B. constraint is needed to prevent a cycle when __adl_begin finds - // begin(span) which does overload resolution on span(Range&&). - template<typename _Rng, - typename _Rng2 = remove_cvref_t<_Rng>, - typename = enable_if_t<!__detail::__is_std_span<_Rng2>::value>> - using iterator_t = decltype(std::__adl_begin(std::declval<_Rng&>())); - // FIXME: use std::iter_value_t - template<typename _Iter> - using iter_value_t = typename iterator_traits<_Iter>::value_type; - // FIXME: use std::derived_from concept - template<typename _Derived, typename _Base> - using derived_from - = __and_<is_base_of<_Base, _Derived>, - is_convertible<const volatile _Derived*, const volatile _Base*>>; - // FIXME: require contiguous_iterator<_Iterator> - template<typename _Iter, - typename _Ref = iter_reference_t<_Iter>, - typename _Traits = iterator_traits<_Iter>, - typename _Tag = typename _Traits::iterator_category> - using __is_compatible_iterator - = __and_<derived_from<_Tag, random_access_iterator_tag>, - is_lvalue_reference<_Ref>, - is_same<iter_value_t<_Iter>, remove_cvref_t<_Ref>>, - __is_compatible<remove_reference_t<_Ref>>>; - - template<typename _Range> - using __is_compatible_range - = __is_compatible_iterator<iterator_t<_Range>>; - public: - template<typename _Range, typename = _Require< - bool_constant<_Extent == dynamic_extent>, - __not_<__detail::__is_std_span<remove_cvref_t<_Range>>>, - __not_<__detail::__is_std_array<remove_cvref_t<_Range>>>, - __not_<is_array<remove_reference_t<_Range>>>, - __is_compatible_range<_Range>>, - typename = decltype(std::__adl_data(std::declval<_Range&>()))> + template<ranges::contiguous_range _Range> + requires (_Extent == dynamic_extent) + && (!__detail::__is_std_span<remove_cvref_t<_Range>>::value) + && (!__detail::__is_std_array<remove_cvref_t<_Range>>::value) + && (!is_array<remove_reference_t<_Range>>::value) + && __detail::__is_compatible_range<_Type, _Range> constexpr span(_Range&& __range) - noexcept(noexcept(::std::__adl_data(__range)) - && noexcept(::std::__adl_size(__range))) - : span(::std::__adl_data(__range), ::std::__adl_size(__range)) + noexcept(noexcept(ranges::data(__range)) + && noexcept(ranges::size(__range))) + : span(ranges::data(__range), ranges::size(__range)) { } - template<typename _ContiguousIterator, typename _Sentinel, typename - = _Require<__not_<is_convertible<_Sentinel, index_type>>, - __is_compatible_iterator<_ContiguousIterator>>> + template<contiguous_iterator _ContiguousIterator, + sized_sentinel_for<_ContiguousIterator> _Sentinel> + requires __detail::__is_compatible_iterator<_Type, _ContiguousIterator> + && (!is_convertible<_Sentinel, size_type>::value) constexpr span(_ContiguousIterator __first, _Sentinel __last) - : _M_extent(static_cast<index_type>(__last - __first)), + : _M_extent(static_cast<size_type>(__last - __first)), _M_ptr(std::to_address(__first)) { if (_Extent != dynamic_extent) __glibcxx_assert((__last - __first) == _Extent); } - template<typename _ContiguousIterator, typename - = _Require<__is_compatible_iterator<_ContiguousIterator>>> + template<contiguous_iterator _ContiguousIterator> + requires __detail::__is_compatible_iterator<_Type, _ContiguousIterator> constexpr - span(_ContiguousIterator __first, index_type __count) + span(_ContiguousIterator __first, size_type __count) noexcept(noexcept(std::to_address(__first))) : _M_extent(__count), _M_ptr(std::to_address(__first)) { __glibcxx_assert(_Extent == dynamic_extent || __count == _Extent); } -#else - private: - template<typename _Container, - typename _DataT = decltype(std::data(std::declval<_Container&>())), - typename _SizeT = decltype(std::size(std::declval<_Container&>()))> - using __is_compatible_container - = __is_compatible<remove_pointer_t<_DataT>>; - - public: - template<typename _Container, typename = _Require< - bool_constant<_Extent == dynamic_extent>, - __not_<__detail::__is_std_span<remove_cv_t<_Container>>>, - __not_<__detail::__is_std_array<remove_cv_t<_Container>>>, - __not_<is_array<_Container>>, - __is_compatible_container<_Container>>> - constexpr - span(_Container& __cont) - noexcept(noexcept(std::data(__cont)) && noexcept(std::size(__cont))) - : _M_extent(std::size(__cont)), _M_ptr(std::data(__cont)) - { } - template<typename _Container, typename = _Require< - bool_constant<_Extent == dynamic_extent>, - __not_<__detail::__is_std_span<remove_cv_t<_Container>>>, - __not_<__detail::__is_std_array<remove_cv_t<_Container>>>, - __not_<is_array<_Container>>, - __is_compatible_container<const _Container>>> - constexpr - span(const _Container& __cont) - noexcept(noexcept(std::data(__cont)) && noexcept(std::size(__cont))) - : _M_extent(std::size(__cont)), _M_ptr(std::data(__cont)) - { } - - constexpr - span(pointer __first, index_type __count) noexcept - : _M_extent(__count), _M_ptr(__first) - { __glibcxx_assert(_Extent == dynamic_extent || __count == _Extent); } - - constexpr - span(pointer __first, pointer __last) noexcept - : span(__first, static_cast<index_type>(__last - __first)) - { } -#endif // P1394 - - template<typename _OType, size_t _OExtent, typename = _Require< - __bool_constant<_Extent == dynamic_extent || _Extent == _OExtent>, - is_convertible<_OType(*)[], _Type(*)[]>>> + template<typename _OType, size_t _OExtent> + requires (_Extent == dynamic_extent || _Extent == _OExtent) + && __detail::__is_compatible_pointer<_Type, _OType> constexpr span(const span<_OType, _OExtent>& __s) noexcept : _M_extent(__s.size()), _M_ptr(__s.data()) @@ -322,11 +244,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // observers - constexpr index_type + constexpr size_type size() const noexcept { return this->_M_extent._M_extent(); } - constexpr index_type + constexpr size_type size_bytes() const noexcept { return this->_M_extent._M_extent() * sizeof(element_type); } @@ -353,7 +275,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } constexpr reference - operator[](index_type __idx) const noexcept + operator[](size_type __idx) const noexcept { static_assert(extent != 0); __glibcxx_assert(__idx < size()); @@ -412,7 +334,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } constexpr span<element_type, dynamic_extent> - first(index_type __count) const noexcept + first(size_type __count) const noexcept { __glibcxx_assert(__count <= size()); return { this->data(), __count }; @@ -430,7 +352,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } constexpr span<element_type, dynamic_extent> - last(index_type __count) const noexcept + last(size_type __count) const noexcept { __glibcxx_assert(__count <= size()); return { this->data() + (this->size() - __count), __count }; @@ -465,7 +387,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } constexpr span<element_type, dynamic_extent> - subspan(index_type __offset, index_type __count = dynamic_extent) const + subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept { __glibcxx_assert(__offset <= size()); @@ -505,27 +427,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION span(const array<_Type, _ArrayExtent>&) -> span<const _Type, _ArrayExtent>; -#ifdef _GLIBCXX_P1394 - template<typename _ContiguousIterator, typename _Sentinel> span(_ContiguousIterator, _Sentinel) - -> span<remove_reference_t< - typename iterator_traits<_ContiguousIterator>::reference>>; + -> span<remove_reference_t<ranges::range_reference_t<_ContiguousIterator>>>; template<typename _Range> span(_Range &&) - -> span<remove_reference_t<typename iterator_traits< - decltype(std::__adl_begin(::std::declval<_Range&>()))>::reference>>; - -#else - - template<typename _Container> - span(_Container&) -> span<typename _Container::value_type>; - - template<typename _Container> - span(const _Container&) -> span<const typename _Container::value_type>; - -#endif // P1394 + -> span<remove_reference_t<ranges::range_reference_t<_Range&>>>; template<typename _Type, size_t _Extent> inline