This should do the same changes for std::insert_iterator, but it currently crashes the compiler (PR 93907). I am not committing this yet.
commit 2db20402125e6136d6768598f5974e917b2622c6 Author: Jonathan Wakely <jwak...@redhat.com> Date: Mon Feb 24 15:50:59 2020 +0000 libstdc++: Add missing bits of P0896R4 pertaining to insert_iterator (PR 93884) This completes the fix for PR 93884. In order to use ranges::iterator_t in std::insert_iterator the definition of ranges::iterator_t and ranges::begin need to be moved, to avoid a circular dependency between <bits/stl_iterator.h> and <bits/range_access.h>. As noted in a FIXME comment, the std::inserter function has the wrong signature because LWG issue 561 was never implemented. That is tracked by PR 93904. PR libstdc++/93884 * include/bits/iterator_concepts.h (__detail::__maybe_borrowed_range) (__cust_access::_Begin, ranges::iterator_t): Move from ... * include/bits/range_access.h: ... here. * include/bits/stl_iterator.h (insert_iterator, inserter): Implement C++20 changes from P0896R4. * testsuite/24_iterators/insert_iterator/pr93884.cc: New test. diff --git a/libstdc++-v3/include/bits/iterator_concepts.h b/libstdc++-v3/include/bits/iterator_concepts.h index 08e622259b4..ae0c207d0d9 100644 --- a/libstdc++-v3/include/bits/iterator_concepts.h +++ b/libstdc++-v3/include/bits/iterator_concepts.h @@ -835,6 +835,90 @@ namespace ranges struct default_sentinel_t { }; inline constexpr default_sentinel_t default_sentinel{}; +namespace ranges +{ + template<typename _Tp> + extern const bool enable_borrowed_range; + + namespace __detail + { + // 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 + { + template<typename _Tp> + constexpr decay_t<_Tp> + __decay_copy(_Tp&& __t) + noexcept(is_nothrow_convertible_v<_Tp, decay_t<_Tp>>) + { return std::forward<_Tp>(__t); } + + template<typename _Tp> + concept __member_begin = requires(_Tp& __t) + { + __t.begin(); + requires input_or_output_iterator<decay_t<decltype(__t.begin())>>; + }; + + void begin(auto&) = delete; + void begin(const auto&) = delete; + + template<typename _Tp> + concept __adl_begin + = std::__detail::__class_or_enum<remove_reference_t<_Tp>> + && requires(_Tp& __t) + { + begin(__t); + requires input_or_output_iterator<decay_t<decltype(begin(__t))>>; + }; + + 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<__detail::__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>); + using _Up = remove_all_extents_t<remove_reference_t<_Tp>>; + static_assert(sizeof(_Up) != 0, "not array of incomplete type"); + return __t + 0; + } + else if constexpr (__member_begin<_Tp>) + return __t.begin(); + else + return begin(__t); + } + }; + } // namespace __cust_access + + template<typename _Tp> + using iterator_t + = decltype(__cust_access::_Begin{}(std::declval<_Tp&>())); + +} // namespace ranges + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // C++20 library concepts diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h index 8b276fd6625..7ae08f58f67 100644 --- a/libstdc++-v3/include/bits/range_access.h +++ b/libstdc++-v3/include/bits/range_access.h @@ -359,13 +359,6 @@ namespace ranges template<typename _Tp, bool _MaxDiff = same_as<_Tp, __max_diff_type>> using __make_unsigned_like_t = conditional_t<_MaxDiff, __max_size_type, make_unsigned_t<_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 @@ -373,64 +366,6 @@ namespace ranges using std::ranges::__detail::__maybe_borrowed_range; using std::__detail::__class_or_enum; - template<typename _Tp> - constexpr decay_t<_Tp> - __decay_copy(_Tp&& __t) - noexcept(is_nothrow_convertible_v<_Tp, decay_t<_Tp>>) - { return std::forward<_Tp>(__t); } - - template<typename _Tp> - concept __member_begin = requires(_Tp& __t) - { - { __decay_copy(__t.begin()) } -> input_or_output_iterator; - }; - - void begin(auto&) = delete; - void begin(const auto&) = delete; - - template<typename _Tp> - concept __adl_begin = __class_or_enum<remove_reference_t<_Tp>> - && requires(_Tp& __t) - { - { __decay_copy(begin(__t)) } -> input_or_output_iterator; - }; - - 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>); - using _Up = remove_all_extents_t<remove_reference_t<_Tp>>; - static_assert(sizeof(_Up) != 0, "not array of incomplete type"); - 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) { @@ -888,9 +823,6 @@ namespace ranges concept borrowed_range = range<_Tp> && __detail::__maybe_borrowed_range<_Tp>; - template<typename _Tp> - using iterator_t = decltype(ranges::begin(std::declval<_Tp&>())); - template<range _Range> using sentinel_t = decltype(ranges::end(std::declval<_Range&>())); diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h index caaa8c483b8..c332343d11b 100644 --- a/libstdc++-v3/include/bits/stl_iterator.h +++ b/libstdc++-v3/include/bits/stl_iterator.h @@ -704,19 +704,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION class insert_iterator : public iterator<output_iterator_tag, void, void, void, void> { +#if __cplusplus <= 201703L + typedef typename _Container::iterator _Iter; +#else + using _Iter = ranges::iterator_t<_Container>; +#endif + protected: +#if __cplusplus <= 201703L _Container* container; - typename _Container::iterator iter; + _Iter iter; +#else + _Container* container = nullptr; + _Iter iter = _Iter(); +#endif public: /// A nested typedef for the type of whatever container you used. typedef _Container container_type; +#if __cplusplus > 201703L + using difference_type = ptrdiff_t; + + insert_iterator() = default; +#endif + /** * The only way to create this %iterator is with a container and an * initial position (a normal %iterator into the container). */ - insert_iterator(_Container& __x, typename _Container::iterator __i) + insert_iterator(_Container& __x, _Iter __i) : container(std::__addressof(__x)), iter(__i) {} /** @@ -782,6 +799,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION insert_iterator& operator++(int) { return *this; } + +#if __cplusplus >= 201703L + template<typename _Cont> + friend insert_iterator<_Cont> + inserter(_Cont&, typename insert_iterator<_Cont>::_Iter); +#endif }; /** @@ -796,6 +819,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * template parameter deduction, making the compiler match the correct * types for you. */ +#if __cplusplus <= 201703L + // FIXME: This version should not exist (PR 93904) template<typename _Container, typename _Iterator> inline insert_iterator<_Container> inserter(_Container& __x, _Iterator __i) @@ -803,6 +828,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return insert_iterator<_Container>(__x, typename _Container::iterator(__i)); } +#else + template<typename _Container> + inline insert_iterator<_Container> + inserter(_Container& __x, typename insert_iterator<_Container>::_Iter __i) + { + return insert_iterator<_Container>(__x, __i); + } +#endif // @} group iterators diff --git a/libstdc++-v3/testsuite/24_iterators/insert_iterator/pr93884.cc b/libstdc++-v3/testsuite/24_iterators/insert_iterator/pr93884.cc new file mode 100644 index 00000000000..e0949e64152 --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/insert_iterator/pr93884.cc @@ -0,0 +1,44 @@ +// Copyright (C) 2020 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. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include <iterator> +#include <algorithm> +#include <set> +#include <testsuite_hooks.h> + +namespace ranges = std::ranges; +namespace views = std::views; + +void +test01() +{ + auto s = std::set<int>{}; + auto i = views::iota(0, 10); + auto o = std::inserter(s, s.begin()); + static_assert(std::output_iterator<decltype(o), int>); + ranges::copy(i, o); + VERIFY( ranges::equal(s, i) ); +} + +int +main() +{ + test01(); +}