diff --git a/stl/inc/ranges b/stl/inc/ranges index 37e9db92824..6cefcb07021 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -10328,27 +10328,26 @@ namespace ranges { && derived_from>::iterator_category, input_iterator_tag> && constructible_from<_Container, iterator_t<_Rng>, iterator_t<_Rng>, _Types...>; + template + concept _Can_emplace_back = requires(_Container& _Cont) { _Cont.emplace_back(_STD declval<_Reference>()); }; + template concept _Can_push_back = requires(_Container& _Cont) { _Cont.push_back(_STD declval<_Reference>()); }; + template + concept _Can_emplace_end = requires(_Container& _Cont) { _Cont.emplace(_Cont.end(), _STD declval<_Reference>()); }; + template concept _Can_insert_end = requires(_Container& _Cont) { _Cont.insert(_Cont.end(), _STD declval<_Reference>()); }; template - concept _Converts_constructible_insertable = + concept _Converts_constructible_appendable = _Ref_converts<_Rng, _Container> && constructible_from<_Container, _Types...> - && (_Can_push_back<_Container, range_reference_t<_Rng>> + && (_Can_emplace_back<_Container, range_reference_t<_Rng>> + || _Can_push_back<_Container, range_reference_t<_Rng>> + || _Can_emplace_end<_Container, range_reference_t<_Rng>> || _Can_insert_end<_Container, range_reference_t<_Rng>>); - template - _NODISCARD constexpr auto _Container_inserter(_Container& _Cont) { - if constexpr (_Can_push_back<_Container, _Reference>) { - return back_insert_iterator{_Cont}; - } else { - return insert_iterator{_Cont, _Cont.end()}; - } - } - _EXPORT_STD template requires (!view<_Container>) _NODISCARD constexpr _Container to(_Rng&& _Range, _Types&&... _Args) { @@ -10361,12 +10360,24 @@ namespace ranges { return _Container(from_range, _STD forward<_Rng>(_Range), _STD forward<_Types>(_Args)...); } else if constexpr (_Converts_and_common_constructible<_Rng, _Container, _Types...>) { return _Container(_RANGES begin(_Range), _RANGES end(_Range), _STD forward<_Types>(_Args)...); - } else if constexpr (_Converts_constructible_insertable<_Rng, _Container, _Types...>) { + } else if constexpr (_Converts_constructible_appendable<_Rng, _Container, _Types...>) { _Container _Cont(_STD forward<_Types>(_Args)...); if constexpr (_Sized_and_reservable<_Rng, _Container>) { _Cont.reserve(static_cast>(_RANGES size(_Range))); } - _RANGES copy(_Range, _Container_inserter>(_Cont)); + for (auto&& _Elem : _Range) { + using _ElemTy = decltype(_Elem); + if constexpr (_Can_emplace_back<_Container, _ElemTy>) { + _Cont.emplace_back(_STD forward<_ElemTy>(_Elem)); + } else if constexpr (_Can_push_back<_Container, _ElemTy>) { + _Cont.push_back(_STD forward<_ElemTy>(_Elem)); + } else if constexpr (_Can_emplace_end<_Container, _ElemTy>) { + _Cont.emplace(_Cont.end(), _STD forward<_ElemTy>(_Elem)); + } else { + _STL_INTERNAL_STATIC_ASSERT(_Can_insert_end<_Container, _ElemTy>); + _Cont.insert(_Cont.end(), _STD forward<_ElemTy>(_Elem)); + } + } return _Cont; } else if constexpr (!_Ref_converts<_Rng, _Container> && input_range>) { const auto _Xform = [](auto&& _Elem) _STATIC_CALL_OPERATOR { diff --git a/tests/std/tests/P1206R7_ranges_to_misc/test.cpp b/tests/std/tests/P1206R7_ranges_to_misc/test.cpp index 8e4e7ece696..0e433f419a4 100644 --- a/tests/std/tests/P1206R7_ranges_to_misc/test.cpp +++ b/tests/std/tests/P1206R7_ranges_to_misc/test.cpp @@ -1,10 +1,13 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include #include #include +#include #include #include +#include #include namespace ranges = std::ranges; @@ -200,6 +203,139 @@ constexpr bool test_lwg3785() { return true; } +enum class restriction_kind { + emplace_back, + push_back, + emplace, + insert, +}; + +template > +class restricted_vector : private std::vector { +private: + using base_type = std::vector; + +public: + using typename base_type::allocator_type; + using typename base_type::const_iterator; + using typename base_type::const_reverse_iterator; + using typename base_type::difference_type; + using typename base_type::iterator; + using typename base_type::pointer; + using typename base_type::reference; + using typename base_type::reverse_iterator; + using typename base_type::size_type; + using typename base_type::value_type; + + restricted_vector() = default; + constexpr explicit restricted_vector(const A& alloc) noexcept : base_type(alloc) {} + constexpr explicit restricted_vector(const size_type n, const A& alloc = A()) : base_type(n, alloc) {} + constexpr restricted_vector(const size_type n, const T& val, const A& alloc = A()) : base_type(n, val, alloc) {} + constexpr restricted_vector(const std::initializer_list il, const A& alloc = A()) : base_type(il, alloc) {} + constexpr restricted_vector(const restricted_vector& other, const A& alloc) : base_type(other, alloc) {} + constexpr restricted_vector(restricted_vector&& other, const A& alloc) noexcept( + std::allocator_traits::is_always_equal::value) + : base_type(std::move(other), alloc) {} + + using base_type::begin; + using base_type::cbegin; + using base_type::cend; + using base_type::crbegin; + using base_type::crend; + using base_type::end; + using base_type::rbegin; + using base_type::rend; + + using base_type::back; + using base_type::front; + using base_type::operator[]; + using base_type::at; + using base_type::data; + + template + constexpr T& emplace_back(Args&&... args) + requires (K == restriction_kind::emplace_back) + { + return base_type::emplace_back(std::forward(args)...); + } + + constexpr void push_back(const T& t) + requires (K == restriction_kind::push_back) + { + base_type::emplace_back(t); + } + constexpr void push_back(T&& t) + requires (K == restriction_kind::push_back) + { + base_type::emplace_back(std::move(t)); + } + + template + constexpr iterator emplace(const const_iterator it, Args&&... args) + requires (K == restriction_kind::emplace) + { + return base_type::emplace(it, std::forward(args)...); + } + + constexpr iterator insert(const const_iterator it, const T& t) + requires (K == restriction_kind::insert) + { + return base_type::emplace(it, t); + } + constexpr iterator insert(const const_iterator it, T&& t) + requires (K == restriction_kind::insert) + { + return base_type::emplace(it, std::move(t)); + } +}; + +template +constexpr void test_lwg4016_per_kind() { + using V = restricted_vector; + { + std::same_as auto vec = std::views::iota(0, 42) | ranges::to(); + assert(ranges::equal(vec, std::views::iota(0, 42))); + } + { + std::same_as auto vec = std::views::iota(0, 42) | ranges::to(std::allocator{}); + assert(ranges::equal(vec, std::views::iota(0, 42))); + } + { + std::same_as auto vec = std::views::empty | ranges::to(std::size_t{42}); + assert(ranges::equal(vec, std::views::repeat(0, 42))); + } + { + std::same_as auto vec = std::views::empty | ranges::to(std::size_t{42}, std::allocator{}); + assert(ranges::equal(vec, std::views::repeat(0, 42))); + } + { + std::same_as auto vec = ranges::to(std::views::iota(0, 42), std::initializer_list{-3, -2, -1}); + assert(ranges::equal(vec, std::views::iota(-3, 42))); + } + { + std::same_as auto vec = + ranges::to(std::views::iota(0, 42), std::initializer_list{-3, -2, -1}, std::allocator{}); + assert(ranges::equal(vec, std::views::iota(-3, 42))); + } + { + std::same_as auto vec = ranges::to(std::views::iota(0, 42), V{-3, -2, -1}, std::allocator{}); + assert(ranges::equal(vec, std::views::iota(-3, 42))); + } + { + V vec0{1, 2, 3}; + std::same_as auto vec = ranges::to(std::views::iota(4, 7), vec0, std::allocator{}); + assert(ranges::equal(vec, std::views::iota(1, 7))); + } +} + +constexpr bool test_lwg4016() { + test_lwg4016_per_kind(); + test_lwg4016_per_kind(); + test_lwg4016_per_kind(); + test_lwg4016_per_kind(); + return true; +} + int main() { test_reservable(); static_assert(test_reservable()); @@ -217,4 +353,7 @@ int main() { test_lwg3785(); static_assert(test_lwg3785()); + + test_lwg4016(); + static_assert(test_lwg4016()); }