aboutsummaryrefslogtreecommitdiff
path: root/libcxx
diff options
context:
space:
mode:
authorvarconst <varconsteq@gmail.com>2023-07-10 17:16:56 -0700
committervarconst <varconsteq@gmail.com>2023-07-18 11:01:10 -0700
commitef70fe4d264d20abdd8476605650479a96a62071 (patch)
tree52f4102af9372ee316ed232f4223967b065b7e16 /libcxx
parent5267ed05bc4612e91409d63b4dbc4e01751acb75 (diff)
downloadllvm-ef70fe4d264d20abdd8476605650479a96a62071.zip
llvm-ef70fe4d264d20abdd8476605650479a96a62071.tar.gz
llvm-ef70fe4d264d20abdd8476605650479a96a62071.tar.bz2
[libc++][ranges] Implement the changes to node-based containers from P1206 (`ranges::to`):
- add the `from_range_t` constructors and the related deduction guides; - add the `insert_range`/`assign_range`/etc. member functions. (Note: this patch is split from https://reviews.llvm.org/D142335) Differential Revision: https://reviews.llvm.org/D149830
Diffstat (limited to 'libcxx')
-rw-r--r--libcxx/include/CMakeLists.txt1
-rw-r--r--libcxx/include/__iterator/ranges_iterator_traits.h42
-rw-r--r--libcxx/include/forward_list89
-rw-r--r--libcxx/include/list91
-rw-r--r--libcxx/include/map125
-rw-r--r--libcxx/include/module.modulemap.in65
-rw-r--r--libcxx/include/set123
-rw-r--r--libcxx/include/unordered_map225
-rw-r--r--libcxx/include/unordered_set222
-rw-r--r--libcxx/test/std/containers/associative/from_range_associative_containers.h304
-rw-r--r--libcxx/test/std/containers/associative/map/map.cons/deduct.pass.cpp39
-rw-r--r--libcxx/test/std/containers/associative/map/map.cons/from_range.pass.cpp51
-rw-r--r--libcxx/test/std/containers/associative/map/map.modifiers/insert_range.pass.cpp40
-rw-r--r--libcxx/test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp39
-rw-r--r--libcxx/test/std/containers/associative/multimap/multimap.cons/from_range.pass.cpp47
-rw-r--r--libcxx/test/std/containers/associative/multimap/multimap.modifiers/insert_range.pass.cpp41
-rw-r--r--libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp36
-rw-r--r--libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp39
-rw-r--r--libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp44
-rw-r--r--libcxx/test/std/containers/associative/set/insert_range.pass.cpp36
-rw-r--r--libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp39
-rw-r--r--libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp50
-rw-r--r--libcxx/test/std/containers/insert_range_helpers.h1
-rw-r--r--libcxx/test/std/containers/insert_range_maps_sets.h415
-rw-r--r--libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp56
-rw-r--r--libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/deduct.pass.cpp19
-rw-r--r--libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/from_range.pass.cpp33
-rw-r--r--libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp38
-rw-r--r--libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_range_after.pass.cpp383
-rw-r--r--libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp38
-rw-r--r--libcxx/test/std/containers/sequences/list/exception_safety.pass.cpp225
-rw-r--r--libcxx/test/std/containers/sequences/list/list.cons/deduct.pass.cpp25
-rw-r--r--libcxx/test/std/containers/sequences/list/list.cons/from_range.pass.cpp34
-rw-r--r--libcxx/test/std/containers/sequences/list/list.modifiers/append_range.pass.cpp38
-rw-r--r--libcxx/test/std/containers/sequences/list/list.modifiers/assign_range.pass.cpp38
-rw-r--r--libcxx/test/std/containers/sequences/list/list.modifiers/insert_range.pass.cpp38
-rw-r--r--libcxx/test/std/containers/sequences/list/list.modifiers/prepend_range.pass.cpp38
-rw-r--r--libcxx/test/std/containers/unord/from_range_unordered_containers.h435
-rw-r--r--libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/deduct.pass.cpp77
-rw-r--r--libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/from_range.pass.cpp57
-rw-r--r--libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_iter_iter.pass.cpp73
-rw-r--r--libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_range.pass.cpp77
-rw-r--r--libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/deduct.pass.cpp77
-rw-r--r--libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/from_range.pass.cpp53
-rw-r--r--libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_iter_iter.pass.cpp116
-rw-r--r--libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_range.pass.cpp121
-rw-r--r--libcxx/test/std/containers/unord/unord.multiset/insert_iter_iter.pass.cpp72
-rw-r--r--libcxx/test/std/containers/unord/unord.multiset/insert_range.pass.cpp73
-rw-r--r--libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/deduct.pass.cpp76
-rw-r--r--libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/from_range.pass.cpp48
-rw-r--r--libcxx/test/std/containers/unord/unord.set/insert_iter_iter.pass.cpp72
-rw-r--r--libcxx/test/std/containers/unord/unord.set/insert_range.pass.cpp72
-rw-r--r--libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/deduct.pass.cpp77
-rw-r--r--libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/from_range.pass.cpp56
-rw-r--r--libcxx/test/support/deduction_guides_sfinae_checks.h132
55 files changed, 4664 insertions, 307 deletions
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 640de9a..cfb367e 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -465,6 +465,7 @@ set(files
__iterator/permutable.h
__iterator/prev.h
__iterator/projected.h
+ __iterator/ranges_iterator_traits.h
__iterator/readable_traits.h
__iterator/reverse_access.h
__iterator/reverse_iterator.h
diff --git a/libcxx/include/__iterator/ranges_iterator_traits.h b/libcxx/include/__iterator/ranges_iterator_traits.h
new file mode 100644
index 0000000..a308641
--- /dev/null
+++ b/libcxx/include/__iterator/ranges_iterator_traits.h
@@ -0,0 +1,42 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ITERATOR_RANGES_ITERATOR_TRAITS_H
+#define _LIBCPP___ITERATOR_RANGES_ITERATOR_TRAITS_H
+
+#include <__config>
+#include <__fwd/pair.h>
+#include <__ranges/concepts.h>
+#include <__type_traits/add_const.h>
+#include <__type_traits/remove_const.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+template <ranges::input_range _Range>
+using __range_key_type = __remove_const_t<typename ranges::range_value_t<_Range>::first_type>;
+
+template <ranges::input_range _Range>
+using __range_mapped_type = typename ranges::range_value_t<_Range>::second_type;
+
+template <ranges::input_range _Range>
+using __range_to_alloc_type =
+ pair<add_const_t<typename ranges::range_value_t<_Range>::first_type>,
+ typename ranges::range_value_t<_Range>::second_type>;
+
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ITERATOR_RANGES_ITERATOR_TRAITS_H
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index aac9d54..6cb7940 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -44,6 +44,8 @@ public:
forward_list(InputIterator first, InputIterator last);
template <class InputIterator>
forward_list(InputIterator first, InputIterator last, const allocator_type& a);
+ template<container-compatible-range<T> R>
+ forward_list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
forward_list(const forward_list& x);
forward_list(const forward_list& x, const allocator_type& a);
forward_list(forward_list&& x)
@@ -63,6 +65,8 @@ public:
template <class InputIterator>
void assign(InputIterator first, InputIterator last);
+ template<container-compatible-range<T> R>
+ void assign_range(R&& rg); // C++23
void assign(size_type n, const value_type& v);
void assign(initializer_list<value_type> il);
@@ -89,6 +93,8 @@ public:
template <class... Args> reference emplace_front(Args&&... args); // reference in C++17
void push_front(const value_type& v);
void push_front(value_type&& v);
+ template<container-compatible-range<T> R>
+ void prepend_range(R&& rg); // C++23
void pop_front();
@@ -100,6 +106,8 @@ public:
template <class InputIterator>
iterator insert_after(const_iterator p,
InputIterator first, InputIterator last);
+ template<container-compatible-range<T> R>
+ iterator insert_range_after(const_iterator position, R&& rg); // C++23
iterator insert_after(const_iterator p, initializer_list<value_type> il);
iterator erase_after(const_iterator p);
@@ -140,6 +148,10 @@ template <class InputIterator, class Allocator = allocator<typename iterator_tra
forward_list(InputIterator, InputIterator, Allocator = Allocator())
-> forward_list<typename iterator_traits<InputIterator>::value_type, Allocator>; // C++17
+template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>>
+ forward_list(from_range_t, R&&, Allocator = Allocator())
+ -> forward_list<ranges::range_value_t<R>, Allocator>; // C++23
+
template <class T, class Allocator>
bool operator==(const forward_list<T, Allocator>& x,
const forward_list<T, Allocator>& y);
@@ -204,6 +216,10 @@ template <class T, class Allocator, class Predicate>
#include <__memory/swap_allocator.h>
#include <__memory/unique_ptr.h>
#include <__memory_resource/polymorphic_allocator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
#include <__type_traits/conditional.h>
#include <__type_traits/is_allocator.h>
#include <__type_traits/is_const.h>
@@ -722,6 +738,15 @@ public:
_LIBCPP_HIDE_FROM_ABI forward_list(_InputIterator __f, _InputIterator __l,
const allocator_type& __a,
__enable_if_t<__has_input_iterator_category<_InputIterator>::value>* = nullptr);
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI forward_list(from_range_t, _Range&& __range,
+ const allocator_type& __a = allocator_type()) : base(__a) {
+ prepend_range(std::forward<_Range>(__range));
+ }
+#endif
+
_LIBCPP_HIDE_FROM_ABI forward_list(const forward_list& __x);
_LIBCPP_HIDE_FROM_ABI forward_list(const forward_list& __x, const __type_identity_t<allocator_type>& __a);
@@ -755,6 +780,15 @@ public:
template <class _InputIterator>
__enable_if_t<__has_input_iterator_category<_InputIterator>::value, void>
_LIBCPP_HIDE_FROM_ABI assign(_InputIterator __f, _InputIterator __l);
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void assign_range(_Range&& __range) {
+ __assign_with_sentinel(ranges::begin(__range), ranges::end(__range));
+ }
+#endif
+
_LIBCPP_HIDE_FROM_ABI void assign(size_type __n, const value_type& __v);
_LIBCPP_INLINE_VISIBILITY
@@ -818,6 +852,14 @@ public:
#endif // _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI void push_front(const value_type& __v);
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void prepend_range(_Range&& __range) {
+ insert_range_after(cbefore_begin(), std::forward<_Range>(__range));
+ }
+#endif
+
_LIBCPP_HIDE_FROM_ABI void pop_front();
#ifndef _LIBCPP_CXX03_LANG
@@ -835,6 +877,18 @@ public:
__enable_if_t<__has_input_iterator_category<_InputIterator>::value, iterator>
insert_after(const_iterator __p, _InputIterator __f, _InputIterator __l);
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ iterator insert_range_after(const_iterator __position, _Range&& __range) {
+ return __insert_after_with_sentinel(__position, ranges::begin(__range), ranges::end(__range));
+ }
+#endif
+
+ template <class _InputIterator, class _Sentinel>
+ _LIBCPP_HIDE_FROM_ABI
+ iterator __insert_after_with_sentinel(const_iterator __p, _InputIterator __f, _Sentinel __l);
+
_LIBCPP_HIDE_FROM_ABI iterator erase_after(const_iterator __p);
_LIBCPP_HIDE_FROM_ABI iterator erase_after(const_iterator __f, const_iterator __l);
@@ -896,6 +950,10 @@ private:
_LIBCPP_HIDE_FROM_ABI void __move_assign(forward_list& __x, false_type);
#endif // _LIBCPP_CXX03_LANG
+ template <class _Iter, class _Sent>
+ _LIBCPP_HIDE_FROM_ABI
+ void __assign_with_sentinel(_Iter __f, _Sent __l);
+
template <class _Compare>
static _LIBCPP_HIDE_FROM_ABI
__node_pointer
@@ -927,6 +985,15 @@ forward_list(_InputIterator, _InputIterator, _Alloc)
-> forward_list<__iter_value_type<_InputIterator>, _Alloc>;
#endif
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+ class _Alloc = allocator<ranges::range_value_t<_Range>>,
+ class = enable_if_t<__is_allocator<_Alloc>::value>
+ >
+forward_list(from_range_t, _Range&&, _Alloc = _Alloc())
+ -> forward_list<ranges::range_value_t<_Range>, _Alloc>;
+#endif
+
template <class _Tp, class _Alloc>
inline
forward_list<_Tp, _Alloc>::forward_list(const allocator_type& __a)
@@ -1107,13 +1174,20 @@ template <class _InputIterator>
__enable_if_t<__has_input_iterator_category<_InputIterator>::value, void>
forward_list<_Tp, _Alloc>::assign(_InputIterator __f, _InputIterator __l)
{
+ __assign_with_sentinel(__f, __l);
+}
+
+template <class _Tp, class _Alloc>
+template <class _Iter, class _Sent>
+_LIBCPP_HIDE_FROM_ABI
+void forward_list<_Tp, _Alloc>::__assign_with_sentinel(_Iter __f, _Sent __l) {
iterator __i = before_begin();
iterator __j = _VSTD::next(__i);
iterator __e = end();
for (; __j != __e && __f != __l; ++__i, (void) ++__j, ++__f)
*__j = *__f;
if (__j == __e)
- insert_after(__i, __f, __l);
+ __insert_after_with_sentinel(__i, std::move(__f), std::move(__l));
else
erase_after(__i, __e);
}
@@ -1307,9 +1381,17 @@ __enable_if_t<__has_input_iterator_category<_InputIterator>::value, typename for
forward_list<_Tp, _Alloc>::insert_after(const_iterator __p,
_InputIterator __f, _InputIterator __l)
{
- using _Guard = __allocation_guard<__node_allocator>;
+ return __insert_after_with_sentinel(__p, std::move(__f), std::move(__l));
+}
+template <class _Tp, class _Alloc>
+template <class _InputIterator, class _Sentinel>
+_LIBCPP_HIDE_FROM_ABI
+typename forward_list<_Tp, _Alloc>::iterator
+forward_list<_Tp, _Alloc>::__insert_after_with_sentinel(const_iterator __p, _InputIterator __f, _Sentinel __l) {
+ using _Guard = __allocation_guard<__node_allocator>;
__begin_node_pointer __r = __p.__get_begin();
+
if (__f != __l)
{
__node_allocator& __a = base::__alloc();
@@ -1321,6 +1403,7 @@ forward_list<_Tp, _Alloc>::insert_after(const_iterator __p,
__first = __h.__release_ptr();
}
__node_pointer __last = __first;
+
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try
{
@@ -1346,10 +1429,12 @@ forward_list<_Tp, _Alloc>::insert_after(const_iterator __p,
throw;
}
#endif // _LIBCPP_HAS_NO_EXCEPTIONS
+
__last->__next_ = __r->__next_;
__r->__next_ = __first;
__r = static_cast<__begin_node_pointer>(__last);
}
+
return iterator(__r);
}
diff --git a/libcxx/include/list b/libcxx/include/list
index bf613b9..2512259 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -46,6 +46,8 @@ public:
list(Iter first, Iter last);
template <class Iter>
list(Iter first, Iter last, const allocator_type& a);
+ template<container-compatible-range<T> R>
+ list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
list(const list& x);
list(const list&, const allocator_type& a);
list(list&& x)
@@ -64,6 +66,8 @@ public:
list& operator=(initializer_list<value_type>);
template <class Iter>
void assign(Iter first, Iter last);
+ template<container-compatible-range<T> R>
+ void assign_range(R&& rg); // C++23
void assign(size_type n, const value_type& t);
void assign(initializer_list<value_type>);
@@ -99,8 +103,12 @@ public:
void pop_back();
void push_front(const value_type& x);
void push_front(value_type&& x);
+ template<container-compatible-range<T> R>
+ void prepend_range(R&& rg); // C++23
void push_back(const value_type& x);
void push_back(value_type&& x);
+ template<container-compatible-range<T> R>
+ void append_range(R&& rg); // C++23
template <class... Args>
iterator emplace(const_iterator position, Args&&... args);
iterator insert(const_iterator position, const value_type& x);
@@ -108,6 +116,8 @@ public:
iterator insert(const_iterator position, size_type n, const value_type& x);
template <class Iter>
iterator insert(const_iterator position, Iter first, Iter last);
+ template<container-compatible-range<T> R>
+ iterator insert_range(const_iterator position, R&& rg); // C++23
iterator insert(const_iterator position, initializer_list<value_type> il);
iterator erase(const_iterator position);
@@ -152,6 +162,10 @@ template <class InputIterator, class Allocator = allocator<typename iterator_tra
list(InputIterator, InputIterator, Allocator = Allocator())
-> list<typename iterator_traits<InputIterator>::value_type, Allocator>; // C++17
+template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>>
+ list(from_range_t, R&&, Allocator = Allocator())
+ -> list<ranges::range_value_t<R>, Allocator>; // C++23
+
template <class T, class Alloc>
bool operator==(const list<T,Alloc>& x, const list<T,Alloc>& y);
template <class T, class Alloc>
@@ -207,6 +221,10 @@ template <class T, class Allocator, class Predicate>
#include <__memory/swap_allocator.h>
#include <__memory/unique_ptr.h>
#include <__memory_resource/polymorphic_allocator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
#include <__type_traits/conditional.h>
#include <__type_traits/is_allocator.h>
#include <__type_traits/is_nothrow_default_constructible.h>
@@ -761,6 +779,14 @@ public:
_LIBCPP_HIDE_FROM_ABI list(_InpIter __f, _InpIter __l, const allocator_type& __a,
__enable_if_t<__has_input_iterator_category<_InpIter>::value>* = 0);
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI list(from_range_t, _Range&& __range,
+ const allocator_type& __a = allocator_type()) : base(__a) {
+ prepend_range(std::forward<_Range>(__range));
+ }
+#endif
+
_LIBCPP_HIDE_FROM_ABI list(const list& __c);
_LIBCPP_HIDE_FROM_ABI list(const list& __c, const __type_identity_t<allocator_type>& __a);
_LIBCPP_INLINE_VISIBILITY
@@ -792,6 +818,15 @@ public:
template <class _InpIter>
_LIBCPP_HIDE_FROM_ABI void assign(_InpIter __f, _InpIter __l,
__enable_if_t<__has_input_iterator_category<_InpIter>::value>* = 0);
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void assign_range(_Range&& __range) {
+ __assign_with_sentinel(ranges::begin(__range), ranges::end(__range));
+ }
+#endif
+
_LIBCPP_HIDE_FROM_ABI void assign(size_type __n, const value_type& __x);
_LIBCPP_INLINE_VISIBILITY
@@ -870,6 +905,20 @@ public:
_LIBCPP_HIDE_FROM_ABI void push_front(value_type&& __x);
_LIBCPP_HIDE_FROM_ABI void push_back(value_type&& __x);
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void prepend_range(_Range&& __range) {
+ insert_range(begin(), std::forward<_Range>(__range));
+ }
+
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void append_range(_Range&& __range) {
+ insert_range(end(), std::forward<_Range>(__range));
+ }
+#endif
+
template <class... _Args>
#if _LIBCPP_STD_VER >= 17
_LIBCPP_HIDE_FROM_ABI reference emplace_front(_Args&&... __args);
@@ -910,6 +959,14 @@ public:
_LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, _InpIter __f, _InpIter __l,
__enable_if_t<__has_input_iterator_category<_InpIter>::value>* = 0);
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ iterator insert_range(const_iterator __position, _Range&& __range) {
+ return __insert_with_sentinel(__position, ranges::begin(__range), ranges::end(__range));
+ }
+#endif
+
_LIBCPP_INLINE_VISIBILITY
void swap(list& __c)
#if _LIBCPP_STD_VER >= 14
@@ -986,6 +1043,14 @@ public:
}
private:
+ template <class _Iterator, class _Sentinel>
+ _LIBCPP_HIDE_FROM_ABI
+ void __assign_with_sentinel(_Iterator __f, _Sentinel __l);
+
+ template <class _Iterator, class _Sentinel>
+ _LIBCPP_HIDE_FROM_ABI
+ iterator __insert_with_sentinel(const_iterator __p, _Iterator __f, _Sentinel __l);
+
_LIBCPP_INLINE_VISIBILITY
static void __link_nodes (__link_pointer __p, __link_pointer __f, __link_pointer __l);
_LIBCPP_INLINE_VISIBILITY
@@ -1020,6 +1085,15 @@ list(_InputIterator, _InputIterator, _Alloc)
-> list<__iter_value_type<_InputIterator>, _Alloc>;
#endif
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+ class _Alloc = allocator<ranges::range_value_t<_Range>>,
+ class = enable_if_t<__is_allocator<_Alloc>::value>
+ >
+list(from_range_t, _Range&&, _Alloc = _Alloc())
+ -> list<ranges::range_value_t<_Range>, _Alloc>;
+#endif
+
// Link in nodes [__f, __l] just prior to __p
template <class _Tp, class _Alloc>
inline
@@ -1225,12 +1299,19 @@ void
list<_Tp, _Alloc>::assign(_InpIter __f, _InpIter __l,
__enable_if_t<__has_input_iterator_category<_InpIter>::value>*)
{
+ __assign_with_sentinel(__f, __l);
+}
+
+template <class _Tp, class _Alloc>
+template <class _Iterator, class _Sentinel>
+_LIBCPP_HIDE_FROM_ABI
+void list<_Tp, _Alloc>::__assign_with_sentinel(_Iterator __f, _Sentinel __l) {
iterator __i = begin();
iterator __e = end();
for (; __f != __l && __i != __e; ++__f, (void) ++__i)
*__i = *__f;
if (__i == __e)
- insert(__e, __f, __l);
+ __insert_with_sentinel(__e, std::move(__f), std::move(__l));
else
erase(__i, __e);
}
@@ -1324,6 +1405,14 @@ typename list<_Tp, _Alloc>::iterator
list<_Tp, _Alloc>::insert(const_iterator __p, _InpIter __f, _InpIter __l,
__enable_if_t<__has_input_iterator_category<_InpIter>::value>*)
{
+ return __insert_with_sentinel(__p, __f, __l);
+}
+
+template <class _Tp, class _Alloc>
+template <class _Iterator, class _Sentinel>
+_LIBCPP_HIDE_FROM_ABI
+typename list<_Tp, _Alloc>::iterator
+list<_Tp, _Alloc>::__insert_with_sentinel(const_iterator __p, _Iterator __f, _Sentinel __l) {
iterator __r(__p.__ptr_);
if (__f != __l)
{
diff --git a/libcxx/include/map b/libcxx/include/map
index 6cc331c..d21f6b9 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -70,6 +70,8 @@ public:
template <class InputIterator>
map(InputIterator first, InputIterator last,
const key_compare& comp, const allocator_type& a);
+ template<container-compatible-range<value_type> R>
+ map(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
map(const map& m);
map(map&& m)
noexcept(
@@ -83,6 +85,9 @@ public:
template <class InputIterator>
map(InputIterator first, InputIterator last, const allocator_type& a)
: map(first, last, Compare(), a) {} // C++14
+ template<container-compatible-range<value_type> R>
+ map(from_range_t, R&& rg, const Allocator& a))
+ : map(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
map(initializer_list<value_type> il, const allocator_type& a)
: map(il, Compare(), a) {} // C++14
~map();
@@ -138,6 +143,8 @@ public:
iterator insert(const_iterator position, P&& p);
template <class InputIterator>
void insert(InputIterator first, InputIterator last);
+ template<container-compatible-range<value_type> R>
+ void insert_range(R&& rg); // C++23
void insert(initializer_list<value_type> il);
node_type extract(const_iterator position); // C++17
@@ -229,6 +236,11 @@ template <class InputIterator,
map(InputIterator, InputIterator, Compare = Compare(), Allocator = Allocator())
-> map<iter_key_t<InputIterator>, iter_val_t<InputIterator>, Compare, Allocator>; // C++17
+template<ranges::input_range R, class Compare = less<range-key-type<R>,
+ class Allocator = allocator<range-to-alloc-type<R>>>
+ map(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+ -> map<range-key-type<R>, range-mapped-type<R>, Compare, Allocator>; // C++23
+
template<class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T>>>
map(initializer_list<pair<const Key, T>>, Compare = Compare(), Allocator = Allocator())
@@ -239,6 +251,10 @@ map(InputIterator, InputIterator, Allocator)
-> map<iter_key_t<InputIterator>, iter_val_t<InputIterator>, less<iter_key_t<InputIterator>>,
Allocator>; // C++17
+template<ranges::input_range R, class Allocator>
+ map(from_range_t, R&&, Allocator)
+ -> map<range-key-type<R>, range-mapped-type<R>, less<range-key-type<R>>, Allocator>; // C++23
+
template<class Key, class T, class Allocator>
map(initializer_list<pair<const Key, T>>, Allocator) -> map<Key, T, less<Key>, Allocator>; // C++17
@@ -338,6 +354,9 @@ public:
template <class InputIterator>
multimap(InputIterator first, InputIterator last, const key_compare& comp,
const allocator_type& a);
+ template<container-compatible-range<value_type> R>
+ multimap(from_range_t, R&& rg,
+ const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
multimap(const multimap& m);
multimap(multimap&& m)
noexcept(
@@ -352,6 +371,9 @@ public:
template <class InputIterator>
multimap(InputIterator first, InputIterator last, const allocator_type& a)
: multimap(first, last, Compare(), a) {} // C++14
+ template<container-compatible-range<value_type> R>
+ multimap(from_range_t, R&& rg, const Allocator& a))
+ : multimap(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
multimap(initializer_list<value_type> il, const allocator_type& a)
: multimap(il, Compare(), a) {} // C++14
~multimap();
@@ -400,6 +422,8 @@ public:
iterator insert(const_iterator position, P&& p);
template <class InputIterator>
void insert(InputIterator first, InputIterator last);
+ template<container-compatible-range<value_type> R>
+ void insert_range(R&& rg); // C++23
void insert(initializer_list<value_type> il);
node_type extract(const_iterator position); // C++17
@@ -474,6 +498,11 @@ template <class InputIterator,
multimap(InputIterator, InputIterator, Compare = Compare(), Allocator = Allocator())
-> multimap<iter_key_t<InputIterator>, iter_val_t<InputIterator>, Compare, Allocator>; // C++17
+template<ranges::input_range R, class Compare = less<range-key-type<R>>,
+ class Allocator = allocator<range-to-alloc-type<R>>>
+ multimap(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+ -> multimap<range-key-type<R>, range-mapped-type<R>, Compare, Allocator>; // C++23
+
template<class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T>>>
multimap(initializer_list<pair<const Key, T>>, Compare = Compare(), Allocator = Allocator())
@@ -484,6 +513,10 @@ multimap(InputIterator, InputIterator, Allocator)
-> multimap<iter_key_t<InputIterator>, iter_val_t<InputIterator>,
less<iter_key_t<InputIterator>>, Allocator>; // C++17
+template<ranges::input_range R, class Allocator>
+ multimap(from_range_t, R&&, Allocator)
+ -> multimap<range-key-type<R>, range-mapped-type<R>, less<range-key-type<R>>, Allocator>; // C++23
+
template<class Key, class T, class Allocator>
multimap(initializer_list<pair<const Key, T>>, Allocator)
-> multimap<Key, T, less<Key>, Allocator>; // C++17
@@ -549,11 +582,15 @@ erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred); // C++20
#include <__functional/operations.h>
#include <__iterator/erase_if_container.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/ranges_iterator_traits.h>
#include <__iterator/reverse_iterator.h>
#include <__memory/addressof.h>
#include <__memory/allocator.h>
#include <__memory_resource/polymorphic_allocator.h>
#include <__node_handle>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
#include <__tree>
#include <__type_traits/is_allocator.h>
#include <__utility/forward.h>
@@ -1079,6 +1116,16 @@ public:
insert(__f, __l);
}
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ map(from_range_t, _Range&& __range, const key_compare& __comp = key_compare(),
+ const allocator_type& __a = allocator_type())
+ : __tree_(__vc(__comp), typename __base::allocator_type(__a)) {
+ insert_range(std::forward<_Range>(__range));
+ }
+#endif
+
#if _LIBCPP_STD_VER >= 14
template <class _InputIterator>
_LIBCPP_INLINE_VISIBILITY
@@ -1086,6 +1133,13 @@ public:
: map(__f, __l, key_compare(), __a) {}
#endif
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ map(from_range_t, _Range&& __range, const allocator_type& __a)
+ : map(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
+#endif
+
_LIBCPP_INLINE_VISIBILITY
map(const map& __m)
: __tree_(__m.__tree_)
@@ -1285,6 +1339,17 @@ public:
insert(__e.__i_, *__f);
}
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void insert_range(_Range&& __range) {
+ const_iterator __end = cend();
+ for (auto&& __element : __range) {
+ insert(__end.__i_, std::forward<decltype(__element)>(__element));
+ }
+ }
+#endif
+
#if _LIBCPP_STD_VER >= 17
template <class... _Args>
@@ -1570,6 +1635,15 @@ template<class _InputIterator, class _Compare = less<__iter_key_type<_InputItera
map(_InputIterator, _InputIterator, _Compare = _Compare(), _Allocator = _Allocator())
-> map<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>, _Compare, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Compare = less<__range_key_type<_Range>>,
+ class _Allocator = allocator<__range_to_alloc_type<_Range>>,
+ class = enable_if_t<!__is_allocator<_Compare>::value, void>,
+ class = enable_if_t<__is_allocator<_Allocator>::value, void>>
+map(from_range_t, _Range&&, _Compare = _Compare(), _Allocator = _Allocator())
+ -> map<__range_key_type<_Range>, __range_mapped_type<_Range>, _Compare, _Allocator>;
+#endif
+
template<class _Key, class _Tp, class _Compare = less<remove_const_t<_Key>>,
class _Allocator = allocator<pair<const _Key, _Tp>>,
class = enable_if_t<!__is_allocator<_Compare>::value, void>,
@@ -1584,6 +1658,13 @@ map(_InputIterator, _InputIterator, _Allocator)
-> map<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>,
less<__iter_key_type<_InputIterator>>, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Allocator,
+ class = enable_if_t<__is_allocator<_Allocator>::value, void>>
+map(from_range_t, _Range&&, _Allocator)
+ -> map<__range_key_type<_Range>, __range_mapped_type<_Range>, less<__range_key_type<_Range>>, _Allocator>;
+#endif
+
template<class _Key, class _Tp, class _Allocator,
class = enable_if_t<__is_allocator<_Allocator>::value, void>>
map(initializer_list<pair<_Key, _Tp>>, _Allocator)
@@ -1875,6 +1956,16 @@ public:
insert(__f, __l);
}
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ multimap(from_range_t, _Range&& __range, const key_compare& __comp = key_compare(),
+ const allocator_type& __a = allocator_type())
+ : __tree_(__vc(__comp), typename __base::allocator_type(__a)) {
+ insert_range(std::forward<_Range>(__range));
+ }
+#endif
+
#if _LIBCPP_STD_VER >= 14
template <class _InputIterator>
_LIBCPP_INLINE_VISIBILITY
@@ -1882,6 +1973,13 @@ public:
: multimap(__f, __l, key_compare(), __a) {}
#endif
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ multimap(from_range_t, _Range&& __range, const allocator_type& __a)
+ : multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
+#endif
+
_LIBCPP_INLINE_VISIBILITY
multimap(const multimap& __m)
: __tree_(__m.__tree_.value_comp(),
@@ -2072,6 +2170,17 @@ public:
__tree_.__insert_multi(__e.__i_, *__f);
}
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void insert_range(_Range&& __range) {
+ const_iterator __end = cend();
+ for (auto&& __element : __range) {
+ __tree_.__insert_multi(__end.__i_, std::forward<decltype(__element)>(__element));
+ }
+ }
+#endif
+
_LIBCPP_INLINE_VISIBILITY
iterator erase(const_iterator __p) {return __tree_.erase(__p.__i_);}
_LIBCPP_INLINE_VISIBILITY
@@ -2256,6 +2365,15 @@ template<class _InputIterator, class _Compare = less<__iter_key_type<_InputItera
multimap(_InputIterator, _InputIterator, _Compare = _Compare(), _Allocator = _Allocator())
-> multimap<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>, _Compare, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Compare = less<__range_key_type<_Range>>,
+ class _Allocator = allocator<__range_to_alloc_type<_Range>>,
+ class = enable_if_t<!__is_allocator<_Compare>::value, void>,
+ class = enable_if_t<__is_allocator<_Allocator>::value, void>>
+multimap(from_range_t, _Range&&, _Compare = _Compare(), _Allocator = _Allocator())
+ -> multimap<__range_key_type<_Range>, __range_mapped_type<_Range>, _Compare, _Allocator>;
+#endif
+
template<class _Key, class _Tp, class _Compare = less<remove_const_t<_Key>>,
class _Allocator = allocator<pair<const _Key, _Tp>>,
class = enable_if_t<!__is_allocator<_Compare>::value, void>,
@@ -2270,6 +2388,13 @@ multimap(_InputIterator, _InputIterator, _Allocator)
-> multimap<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>,
less<__iter_key_type<_InputIterator>>, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Allocator,
+ class = enable_if_t<__is_allocator<_Allocator>::value, void>>
+multimap(from_range_t, _Range&&, _Allocator)
+ -> multimap<__range_key_type<_Range>, __range_mapped_type<_Range>, less<__range_key_type<_Range>>, _Allocator>;
+#endif
+
template<class _Key, class _Tp, class _Allocator,
class = enable_if_t<__is_allocator<_Allocator>::value, void>>
multimap(initializer_list<pair<_Key, _Tp>>, _Allocator)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 19db0c8..88de198 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1146,55 +1146,56 @@ module std [system] {
export type_traits.is_reference
export type_traits.remove_cvref
}
- module counted_iterator { private header "__iterator/counted_iterator.h" }
- module data { private header "__iterator/data.h" }
- module default_sentinel { private header "__iterator/default_sentinel.h" }
- module distance {
+ module counted_iterator { private header "__iterator/counted_iterator.h" }
+ module data { private header "__iterator/data.h" }
+ module default_sentinel { private header "__iterator/default_sentinel.h" }
+ module distance {
private header "__iterator/distance.h"
export ranges.__ranges.size
}
- module empty { private header "__iterator/empty.h" }
- module erase_if_container { private header "__iterator/erase_if_container.h" }
- module front_insert_iterator { private header "__iterator/front_insert_iterator.h" }
- module incrementable_traits { private header "__iterator/incrementable_traits.h" }
- module indirectly_comparable { private header "__iterator/indirectly_comparable.h" }
- module insert_iterator { private header "__iterator/insert_iterator.h" }
- module istream_iterator { private header "__iterator/istream_iterator.h" }
- module istreambuf_iterator { private header "__iterator/istreambuf_iterator.h" }
- module iter_move { private header "__iterator/iter_move.h" }
- module iter_swap { private header "__iterator/iter_swap.h" }
- module iterator { private header "__iterator/iterator.h" }
- module iterator_traits {
+ module empty { private header "__iterator/empty.h" }
+ module erase_if_container { private header "__iterator/erase_if_container.h" }
+ module front_insert_iterator { private header "__iterator/front_insert_iterator.h" }
+ module incrementable_traits { private header "__iterator/incrementable_traits.h" }
+ module indirectly_comparable { private header "__iterator/indirectly_comparable.h" }
+ module insert_iterator { private header "__iterator/insert_iterator.h" }
+ module istream_iterator { private header "__iterator/istream_iterator.h" }
+ module istreambuf_iterator { private header "__iterator/istreambuf_iterator.h" }
+ module iter_move { private header "__iterator/iter_move.h" }
+ module iter_swap { private header "__iterator/iter_swap.h" }
+ module iterator { private header "__iterator/iterator.h" }
+ module iterator_traits {
private header "__iterator/iterator_traits.h"
export type_traits.is_primary_template
}
- module iterator_with_data { private header "__iterator/iterator_with_data.h" }
+ module iterator_with_data { private header "__iterator/iterator_with_data.h" }
module mergeable {
private header "__iterator/mergeable.h"
export functional.__functional.ranges_operations
}
- module move_iterator { private header "__iterator/move_iterator.h" }
- module move_sentinel { private header "__iterator/move_sentinel.h" }
- module next { private header "__iterator/next.h" }
- module ostream_iterator { private header "__iterator/ostream_iterator.h" }
- module ostreambuf_iterator {
+ module move_iterator { private header "__iterator/move_iterator.h" }
+ module move_sentinel { private header "__iterator/move_sentinel.h" }
+ module next { private header "__iterator/next.h" }
+ module ostream_iterator { private header "__iterator/ostream_iterator.h" }
+ module ostreambuf_iterator {
private header "__iterator/ostreambuf_iterator.h"
export iosfwd
}
- module permutable { private header "__iterator/permutable.h" }
- module prev { private header "__iterator/prev.h" }
- module projected { private header "__iterator/projected.h" }
- module readable_traits { private header "__iterator/readable_traits.h" }
- module reverse_access { private header "__iterator/reverse_access.h" }
- module reverse_iterator { private header "__iterator/reverse_iterator.h" }
- module segmented_iterator { private header "__iterator/segmented_iterator.h" }
- module size { private header "__iterator/size.h" }
+ module permutable { private header "__iterator/permutable.h" }
+ module prev { private header "__iterator/prev.h" }
+ module projected { private header "__iterator/projected.h" }
+ module ranges_iterator_traits { private header "__iterator/ranges_iterator_traits.h" }
+ module readable_traits { private header "__iterator/readable_traits.h" }
+ module reverse_access { private header "__iterator/reverse_access.h" }
+ module reverse_iterator { private header "__iterator/reverse_iterator.h" }
+ module segmented_iterator { private header "__iterator/segmented_iterator.h" }
+ module size { private header "__iterator/size.h" }
module sortable {
private header "__iterator/sortable.h"
export functional.__functional.ranges_operations
}
- module unreachable_sentinel { private header "__iterator/unreachable_sentinel.h" }
- module wrap_iter { private header "__iterator/wrap_iter.h" }
+ module unreachable_sentinel { private header "__iterator/unreachable_sentinel.h" }
+ module wrap_iter { private header "__iterator/wrap_iter.h" }
}
}
module latch {
diff --git a/libcxx/include/set b/libcxx/include/set
index 76489e8..e44f33e 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -56,6 +56,8 @@ public:
template <class InputIterator>
set(InputIterator first, InputIterator last, const value_compare& comp,
const allocator_type& a);
+ template<container-compatible-range<value_type> R>
+ set(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
set(const set& s);
set(set&& s)
noexcept(
@@ -70,6 +72,9 @@ public:
template <class InputIterator>
set(InputIterator first, InputIterator last, const allocator_type& a)
: set(first, last, Compare(), a) {} // C++14
+ template<container-compatible-range<value_type> R>
+ set(from_range_t, R&& rg, const Allocator& a))
+ : set(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
set(initializer_list<value_type> il, const allocator_type& a)
: set(il, Compare(), a) {} // C++14
~set();
@@ -114,6 +119,8 @@ public:
iterator insert(const_iterator position, value_type&& v);
template <class InputIterator>
void insert(InputIterator first, InputIterator last);
+ template<container-compatible-range<value_type> R>
+ void insert_range(R&& rg); // C++23
void insert(initializer_list<value_type> il);
node_type extract(const_iterator position); // C++17
@@ -190,6 +197,11 @@ set(InputIterator, InputIterator,
Compare = Compare(), Allocator = Allocator())
-> set<typename iterator_traits<InputIterator>::value_type, Compare, Allocator>; // C++17
+template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>,
+ class Allocator = allocator<ranges::range_value_t<R>>>
+ set(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+ -> set<ranges::range_value_t<R>, Compare, Allocator>; // C++23
+
template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>>
set(initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
-> set<Key, Compare, Allocator>; // C++17
@@ -199,6 +211,10 @@ set(InputIterator, InputIterator, Allocator)
-> set<typename iterator_traits<InputIterator>::value_type,
less<typename iterator_traits<InputIterator>::value_type>, Allocator>; // C++17
+template<ranges::input_range R, class Allocator>
+ set(from_range_t, R&&, Allocator)
+ -> set<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>; // C++23
+
template<class Key, class Allocator>
set(initializer_list<Key>, Allocator) -> set<Key, less<Key>, Allocator>; // C++17
@@ -284,6 +300,9 @@ public:
template <class InputIterator>
multiset(InputIterator first, InputIterator last,
const value_compare& comp, const allocator_type& a);
+ template<container-compatible-range<value_type> R>
+ multiset(from_range_t, R&& rg,
+ const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
multiset(const multiset& s);
multiset(multiset&& s)
noexcept(
@@ -298,6 +317,9 @@ public:
template <class InputIterator>
multiset(InputIterator first, InputIterator last, const allocator_type& a)
: set(first, last, Compare(), a) {} // C++14
+ template<container-compatible-range<value_type> R>
+ multiset(from_range_t, R&& rg, const Allocator& a))
+ : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
multiset(initializer_list<value_type> il, const allocator_type& a)
: set(il, Compare(), a) {} // C++14
~multiset();
@@ -342,6 +364,8 @@ public:
iterator insert(const_iterator position, value_type&& v);
template <class InputIterator>
void insert(InputIterator first, InputIterator last);
+ template<container-compatible-range<value_type> R>
+ void insert_range(R&& rg); // C++23
void insert(initializer_list<value_type> il);
node_type extract(const_iterator position); // C++17
@@ -419,6 +443,11 @@ multiset(InputIterator, InputIterator,
Compare = Compare(), Allocator = Allocator())
-> multiset<typename iterator_traits<InputIterator>::value_type, Compare, Allocator>; // C++17
+template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>,
+ class Allocator = allocator<ranges::range_value_t<R>>>
+ multiset(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+ -> multiset<ranges::range_value_t<R>, Compare, Allocator>;
+
template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>>
multiset(initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
-> multiset<Key, Compare, Allocator>; // C++17
@@ -428,6 +457,10 @@ multiset(InputIterator, InputIterator, Allocator)
-> multiset<typename iterator_traits<InputIterator>::value_type,
less<typename iterator_traits<InputIterator>::value_type>, Allocator>; // C++17
+template<ranges::input_range R, class Allocator>
+ multiset(from_range_t, R&&, Allocator)
+ -> multiset<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>;
+
template<class Key, class Allocator>
multiset(initializer_list<Key>, Allocator) -> multiset<Key, less<Key>, Allocator>; // C++17
@@ -489,10 +522,14 @@ erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred); // C++20
#include <__functional/operations.h>
#include <__iterator/erase_if_container.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/ranges_iterator_traits.h>
#include <__iterator/reverse_iterator.h>
#include <__memory/allocator.h>
#include <__memory_resource/polymorphic_allocator.h>
#include <__node_handle>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
#include <__tree>
#include <__type_traits/is_allocator.h>
#include <__utility/forward.h>
@@ -603,6 +640,16 @@ public:
insert(__f, __l);
}
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ set(from_range_t, _Range&& __range, const key_compare& __comp = key_compare(),
+ const allocator_type& __a = allocator_type())
+ : __tree_(__comp, __a) {
+ insert_range(std::forward<_Range>(__range));
+ }
+#endif
+
#if _LIBCPP_STD_VER >= 14
template <class _InputIterator>
_LIBCPP_INLINE_VISIBILITY
@@ -610,6 +657,13 @@ public:
: set(__f, __l, key_compare(), __a) {}
#endif
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ set(from_range_t, _Range&& __range, const allocator_type& __a)
+ : set(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
+#endif
+
_LIBCPP_INLINE_VISIBILITY
set(const set& __s)
: __tree_(__s.__tree_)
@@ -752,6 +806,17 @@ public:
__tree_.__insert_unique(__e, *__f);
}
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void insert_range(_Range&& __range) {
+ const_iterator __end = cend();
+ for (auto&& __element : __range) {
+ __tree_.__insert_unique(__end, std::forward<decltype(__element)>(__element));
+ }
+ }
+#endif
+
#ifndef _LIBCPP_CXX03_LANG
_LIBCPP_INLINE_VISIBILITY
pair<iterator,bool> insert(value_type&& __v)
@@ -947,6 +1012,15 @@ template<class _InputIterator,
set(_InputIterator, _InputIterator, _Compare = _Compare(), _Allocator = _Allocator())
-> set<__iter_value_type<_InputIterator>, _Compare, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Compare = less<ranges::range_value_t<_Range>>,
+ class _Allocator = allocator<ranges::range_value_t<_Range>>,
+ class = enable_if_t<__is_allocator<_Allocator>::value, void>,
+ class = enable_if_t<!__is_allocator<_Compare>::value, void>>
+set(from_range_t, _Range&&, _Compare = _Compare(), _Allocator = _Allocator())
+ -> set<ranges::range_value_t<_Range>, _Compare, _Allocator>;
+#endif
+
template<class _Key, class _Compare = less<_Key>,
class _Allocator = allocator<_Key>,
class = enable_if_t<!__is_allocator<_Compare>::value, void>,
@@ -961,6 +1035,13 @@ set(_InputIterator, _InputIterator, _Allocator)
-> set<__iter_value_type<_InputIterator>,
less<__iter_value_type<_InputIterator>>, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Allocator,
+ class = enable_if_t<__is_allocator<_Allocator>::value, void>>
+set(from_range_t, _Range&&, _Allocator)
+ -> set<ranges::range_value_t<_Range>, less<ranges::range_value_t<_Range>>, _Allocator>;
+#endif
+
template<class _Key, class _Allocator,
class = enable_if_t<__is_allocator<_Allocator>::value, void>>
set(initializer_list<_Key>, _Allocator)
@@ -1160,6 +1241,21 @@ public:
insert(__f, __l);
}
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ multiset(from_range_t, _Range&& __range, const key_compare& __comp = key_compare(),
+ const allocator_type& __a = allocator_type())
+ : __tree_(__comp, __a) {
+ insert_range(std::forward<_Range>(__range));
+ }
+
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ multiset(from_range_t, _Range&& __range, const allocator_type& __a)
+ : multiset(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
+#endif
+
_LIBCPP_INLINE_VISIBILITY
multiset(const multiset& __s)
: __tree_(__s.__tree_.value_comp(),
@@ -1301,6 +1397,17 @@ public:
__tree_.__insert_multi(__e, *__f);
}
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void insert_range(_Range&& __range) {
+ const_iterator __end = cend();
+ for (auto&& __element : __range) {
+ __tree_.__insert_multi(__end, std::forward<decltype(__element)>(__element));
+ }
+ }
+#endif
+
#ifndef _LIBCPP_CXX03_LANG
_LIBCPP_INLINE_VISIBILITY
iterator insert(value_type&& __v)
@@ -1496,6 +1603,15 @@ template<class _InputIterator,
multiset(_InputIterator, _InputIterator, _Compare = _Compare(), _Allocator = _Allocator())
-> multiset<__iter_value_type<_InputIterator>, _Compare, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Compare = less<ranges::range_value_t<_Range>>,
+ class _Allocator = allocator<ranges::range_value_t<_Range>>,
+ class = enable_if_t<__is_allocator<_Allocator>::value, void>,
+ class = enable_if_t<!__is_allocator<_Compare>::value, void>>
+multiset(from_range_t, _Range&&, _Compare = _Compare(), _Allocator = _Allocator())
+ -> multiset<ranges::range_value_t<_Range>, _Compare, _Allocator>;
+#endif
+
template<class _Key, class _Compare = less<_Key>,
class _Allocator = allocator<_Key>,
class = enable_if_t<__is_allocator<_Allocator>::value, void>,
@@ -1510,6 +1626,13 @@ multiset(_InputIterator, _InputIterator, _Allocator)
-> multiset<__iter_value_type<_InputIterator>,
less<__iter_value_type<_InputIterator>>, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Allocator,
+ class = enable_if_t<__is_allocator<_Allocator>::value, void>>
+multiset(from_range_t, _Range&&, _Allocator)
+ -> multiset<ranges::range_value_t<_Range>, less<ranges::range_value_t<_Range>>, _Allocator>;
+#endif
+
template<class _Key, class _Allocator,
class = enable_if_t<__is_allocator<_Allocator>::value, void>>
multiset(initializer_list<_Key>, _Allocator)
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 8052d10..4772667 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -59,6 +59,11 @@ public:
size_type n = 0, const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
+ template<container-compatible-range<value_type> R>
+ unordered_map(from_range_t, R&& rg, size_type n = see below,
+ const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+ const allocator_type& a = allocator_type()); // C++23
+
explicit unordered_map(const allocator_type&);
unordered_map(const unordered_map&);
unordered_map(const unordered_map&, const Allocator&);
@@ -82,6 +87,12 @@ public:
unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a)
: unordered_map(f, l, n, hf, key_equal(), a) {} // C++14
+ template<container-compatible-range<value_type> R>
+ unordered_map(from_range_t, R&& rg, size_type n, const allocator_type& a)
+ : unordered_map(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+ template<container-compatible-range<value_type> R>
+ unordered_map(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+ : unordered_map(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { } // C++23
unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a)
: unordered_map(il, n, hasher(), key_equal(), a) {} // C++14
unordered_map(initializer_list<value_type> il, size_type n, const hasher& hf,
@@ -122,6 +133,8 @@ public:
iterator insert(const_iterator hint, P&& obj);
template <class InputIterator>
void insert(InputIterator first, InputIterator last);
+ template<container-compatible-range<value_type> R>
+ void insert_range(R&& rg); // C++23
void insert(initializer_list<value_type>);
node_type extract(const_iterator position); // C++17
@@ -224,6 +237,13 @@ unordered_map(InputIterator, InputIterator, typename see below::size_type = see
-> unordered_map<iter_key_t<InputIterator>, iter_value_t<InputIterator>, Hash, Pred,
Allocator>; // C++17
+template<ranges::input_range R, class Hash = hash<range-key-type<R>>,
+ class Pred = equal_to<range-key-type<R>>,
+ class Allocator = allocator<range-to-alloc-type<R>>>
+ unordered_map(from_range_t, R&&, typename see below::size_type = see below,
+ Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+ -> unordered_map<range-key-type<R>, range-mapped-type<R>, Hash, Pred, Allocator>; // C++23
+
template<class Key, class T, class Hash = hash<Key>,
class Pred = equal_to<Key>, class Allocator = allocator<pair<const Key, T>>>
unordered_map(initializer_list<pair<const Key, T>>, typename see below::size_type = see below,
@@ -245,6 +265,21 @@ unordered_map(InputIterator, InputIterator, typename see below::size_type, Hash,
-> unordered_map<iter_key_t<InputIterator>, iter_val_t<InputIterator>, Hash,
equal_to<iter_key_t<InputIterator>>, Allocator>; // C++17
+template<ranges::input_range R, class Allocator>
+ unordered_map(from_range_t, R&&, typename see below::size_type, Allocator)
+ -> unordered_map<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+ equal_to<range-key-type<R>>, Allocator>; // C++23
+
+template<ranges::input_range R, class Allocator>
+ unordered_map(from_range_t, R&&, Allocator)
+ -> unordered_map<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+ equal_to<range-key-type<R>>, Allocator>; // C++23
+
+template<ranges::input_range R, class Hash, class Allocator>
+ unordered_map(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+ -> unordered_map<range-key-type<R>, range-mapped-type<R>, Hash,
+ equal_to<range-key-type<R>>, Allocator>; // C++23
+
template<class Key, class T, typename Allocator>
unordered_map(initializer_list<pair<const Key, T>>, typename see below::size_type, Allocator)
-> unordered_map<Key, T, hash<Key>, equal_to<Key>, Allocator>; // C++17
@@ -311,6 +346,10 @@ public:
size_type n = 0, const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
+ template<container-compatible-range<value_type> R>
+ unordered_multimap(from_range_t, R&& rg, size_type n = see below,
+ const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+ const allocator_type& a = allocator_type()); // C++23
explicit unordered_multimap(const allocator_type&);
unordered_multimap(const unordered_multimap&);
unordered_multimap(const unordered_multimap&, const Allocator&);
@@ -334,6 +373,12 @@ public:
unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a)
: unordered_multimap(f, l, n, hf, key_equal(), a) {} // C++14
+ template<container-compatible-range<value_type> R>
+ unordered_multimap(from_range_t, R&& rg, size_type n, const allocator_type& a)
+ : unordered_multimap(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+ template<container-compatible-range<value_type> R>
+ unordered_multimap(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+ : unordered_multimap(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { } // C++23
unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a)
: unordered_multimap(il, n, hasher(), key_equal(), a) {} // C++14
unordered_multimap(initializer_list<value_type> il, size_type n, const hasher& hf,
@@ -374,6 +419,8 @@ public:
iterator insert(const_iterator hint, P&& obj);
template <class InputIterator>
void insert(InputIterator first, InputIterator last);
+ template<container-compatible-range<value_type> R>
+ void insert_range(R&& rg); // C++23
void insert(initializer_list<value_type>);
node_type extract(const_iterator position); // C++17
@@ -453,6 +500,13 @@ unordered_multimap(InputIterator, InputIterator, typename see below::size_type =
-> unordered_multimap<iter_key_t<InputIterator>, iter_value_t<InputIterator>, Hash, Pred,
Allocator>; // C++17
+template<ranges::input_range R, class Hash = hash<range-key-type<R>>,
+ class Pred = equal_to<range-key-type<R>>,
+ class Allocator = allocator<range-to-alloc-type<R>>>
+ unordered_multimap(from_range_t, R&&, typename see below::size_type = see below,
+ Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+ -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, Hash, Pred, Allocator>; // C++23
+
template<class Key, class T, class Hash = hash<Key>,
class Pred = equal_to<Key>, class Allocator = allocator<pair<const Key, T>>>
unordered_multimap(initializer_list<pair<const Key, T>>, typename see below::size_type = see below,
@@ -474,6 +528,21 @@ unordered_multimap(InputIterator, InputIterator, typename see below::size_type,
-> unordered_multimap<iter_key_t<InputIterator>, iter_val_t<InputIterator>, Hash,
equal_to<iter_key_t<InputIterator>>, Allocator>; // C++17
+template<ranges::input_range R, class Allocator>
+ unordered_multimap(from_range_t, R&&, typename see below::size_type, Allocator)
+ -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+ equal_to<range-key-type<R>>, Allocator>; // C++23
+
+template<ranges::input_range R, class Allocator>
+ unordered_multimap(from_range_t, R&&, Allocator)
+ -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+ equal_to<range-key-type<R>>, Allocator>; // C++23
+
+template<ranges::input_range R, class Hash, class Allocator>
+ unordered_multimap(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+ -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, Hash,
+ equal_to<range-key-type<R>>, Allocator>; // C++23
+
template<class Key, class T, typename Allocator>
unordered_multimap(initializer_list<pair<const Key, T>>, typename see below::size_type, Allocator)
-> unordered_multimap<Key, T, hash<Key>, equal_to<Key>, Allocator>; // C++17
@@ -524,10 +593,14 @@ template <class Key, class T, class Hash, class Pred, class Alloc>
#include <__iterator/distance.h>
#include <__iterator/erase_if_container.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/ranges_iterator_traits.h>
#include <__memory/addressof.h>
#include <__memory/allocator.h>
#include <__memory_resource/polymorphic_allocator.h>
#include <__node_handle>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
#include <__type_traits/is_allocator.h>
#include <__type_traits/type_identity.h>
#include <__utility/forward.h>
@@ -1111,6 +1184,21 @@ public:
size_type __n, const hasher& __hf,
const key_equal& __eql,
const allocator_type& __a);
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ unordered_map(from_range_t, _Range&& __range, size_type __n = /*implementation-defined*/0,
+ const hasher& __hf = hasher(), const key_equal& __eql = key_equal(),
+ const allocator_type& __a = allocator_type())
+ : __table_(__hf, __eql, typename __table::allocator_type(__a)) {
+ if (__n > 0) {
+ __table_.__rehash_unique(__n);
+ }
+ insert_range(std::forward<_Range>(__range));
+ }
+#endif
+
_LIBCPP_INLINE_VISIBILITY
explicit unordered_map(const allocator_type& __a);
_LIBCPP_HIDE_FROM_ABI unordered_map(const unordered_map& __u);
@@ -1143,6 +1231,19 @@ public:
unordered_map(_InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf,
const allocator_type& __a)
: unordered_map(__first, __last, __n, __hf, key_equal(), __a) {}
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ unordered_map(from_range_t, _Range&& __range, size_type __n, const allocator_type& __a)
+ : unordered_map(from_range, std::forward<_Range>(__range), __n, hasher(), key_equal(), __a) {}
+
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ unordered_map(from_range_t, _Range&& __range, size_type __n, const hasher& __hf, const allocator_type& __a)
+ : unordered_map(from_range, std::forward<_Range>(__range), __n, __hf, key_equal(), __a) {}
+#endif
+
_LIBCPP_INLINE_VISIBILITY
unordered_map(initializer_list<value_type> __il, size_type __n, const allocator_type& __a)
: unordered_map(__il, __n, hasher(), key_equal(), __a) {}
@@ -1217,6 +1318,16 @@ public:
_LIBCPP_INLINE_VISIBILITY
void insert(_InputIterator __first, _InputIterator __last);
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void insert_range(_Range&& __range) {
+ for (auto&& __element : __range) {
+ __table_.__insert_unique(std::forward<decltype(__element)>(__element));
+ }
+ }
+#endif
+
#ifndef _LIBCPP_CXX03_LANG
_LIBCPP_INLINE_VISIBILITY
void insert(initializer_list<value_type> __il)
@@ -1528,6 +1639,20 @@ unordered_map(_InputIterator, _InputIterator, typename allocator_traits<_Allocat
_Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
-> unordered_map<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>, _Hash, _Pred, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+ class _Hash = hash<__range_key_type<_Range>>,
+ class _Pred = equal_to<__range_key_type<_Range>>,
+ class _Allocator = allocator<__range_to_alloc_type<_Range>>,
+ class = enable_if_t<!__is_allocator<_Hash>::value>,
+ class = enable_if_t<!is_integral<_Hash>::value>,
+ class = enable_if_t<!__is_allocator<_Pred>::value>,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_map(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type = 0,
+ _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
+ -> unordered_map<__range_key_type<_Range>, __range_mapped_type<_Range>, _Hash, _Pred, _Allocator>; // C++23
+#endif
+
template<class _Key, class _Tp, class _Hash = hash<remove_const_t<_Key>>,
class _Pred = equal_to<remove_const_t<_Key>>,
class _Allocator = allocator<pair<const _Key, _Tp>>,
@@ -1562,6 +1687,30 @@ unordered_map(_InputIterator, _InputIterator, typename allocator_traits<_Allocat
-> unordered_map<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>,
_Hash, equal_to<__iter_key_type<_InputIterator>>, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+
+template <ranges::input_range _Range, class _Allocator,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_map(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Allocator)
+ -> unordered_map<__range_key_type<_Range>, __range_mapped_type<_Range>, hash<__range_key_type<_Range>>,
+ equal_to<__range_key_type<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Allocator,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_map(from_range_t, _Range&&, _Allocator)
+ -> unordered_map<__range_key_type<_Range>, __range_mapped_type<_Range>, hash<__range_key_type<_Range>>,
+ equal_to<__range_key_type<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Hash, class _Allocator,
+ class = enable_if_t<!__is_allocator<_Hash>::value>,
+ class = enable_if_t<!is_integral<_Hash>::value>,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_map(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Hash, _Allocator)
+ -> unordered_map<__range_key_type<_Range>, __range_mapped_type<_Range>, _Hash,
+ equal_to<__range_key_type<_Range>>, _Allocator>;
+
+#endif
+
template<class _Key, class _Tp, class _Allocator,
class = enable_if_t<__is_allocator<_Allocator>::value>>
unordered_map(initializer_list<pair<_Key, _Tp>>, typename allocator_traits<_Allocator>::size_type, _Allocator)
@@ -1950,6 +2099,21 @@ private:
size_type __n, const hasher& __hf,
const key_equal& __eql,
const allocator_type& __a);
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ unordered_multimap(from_range_t, _Range&& __range, size_type __n = /*implementation-defined*/0,
+ const hasher& __hf = hasher(), const key_equal& __eql = key_equal(),
+ const allocator_type& __a = allocator_type())
+ : __table_(__hf, __eql, typename __table::allocator_type(__a)) {
+ if (__n > 0) {
+ __table_.__rehash_multi(__n);
+ }
+ insert_range(std::forward<_Range>(__range));
+ }
+#endif
+
_LIBCPP_INLINE_VISIBILITY
explicit unordered_multimap(const allocator_type& __a);
_LIBCPP_HIDE_FROM_ABI unordered_multimap(const unordered_multimap& __u);
@@ -1983,6 +2147,19 @@ private:
unordered_multimap(_InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf,
const allocator_type& __a)
: unordered_multimap(__first, __last, __n, __hf, key_equal(), __a) {}
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ unordered_multimap(from_range_t, _Range&& __range, size_type __n, const allocator_type& __a)
+ : unordered_multimap(from_range, std::forward<_Range>(__range), __n, hasher(), key_equal(), __a) {}
+
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ unordered_multimap(from_range_t, _Range&& __range, size_type __n, const hasher& __hf, const allocator_type& __a)
+ : unordered_multimap(from_range, std::forward<_Range>(__range), __n, __hf, key_equal(), __a) {}
+#endif
+
_LIBCPP_INLINE_VISIBILITY
unordered_multimap(initializer_list<value_type> __il, size_type __n, const allocator_type& __a)
: unordered_multimap(__il, __n, hasher(), key_equal(), __a) {}
@@ -2056,6 +2233,16 @@ private:
_LIBCPP_INLINE_VISIBILITY
void insert(_InputIterator __first, _InputIterator __last);
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void insert_range(_Range&& __range) {
+ for (auto&& __element : __range) {
+ __table_.__insert_multi(std::forward<decltype(__element)>(__element));
+ }
+ }
+#endif
+
#ifndef _LIBCPP_CXX03_LANG
_LIBCPP_INLINE_VISIBILITY
void insert(initializer_list<value_type> __il)
@@ -2276,6 +2463,20 @@ unordered_multimap(_InputIterator, _InputIterator, typename allocator_traits<_Al
_Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
-> unordered_multimap<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>, _Hash, _Pred, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+ class _Hash = hash<__range_key_type<_Range>>,
+ class _Pred = equal_to<__range_key_type<_Range>>,
+ class _Allocator = allocator<__range_to_alloc_type<_Range>>,
+ class = enable_if_t<!__is_allocator<_Hash>::value>,
+ class = enable_if_t<!is_integral<_Hash>::value>,
+ class = enable_if_t<!__is_allocator<_Pred>::value>,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multimap(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type = 0,
+ _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
+ -> unordered_multimap<__range_key_type<_Range>, __range_mapped_type<_Range>, _Hash, _Pred, _Allocator>;
+#endif
+
template<class _Key, class _Tp, class _Hash = hash<remove_const_t<_Key>>,
class _Pred = equal_to<remove_const_t<_Key>>,
class _Allocator = allocator<pair<const _Key, _Tp>>,
@@ -2310,6 +2511,30 @@ unordered_multimap(_InputIterator, _InputIterator, typename allocator_traits<_Al
-> unordered_multimap<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>,
_Hash, equal_to<__iter_key_type<_InputIterator>>, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+
+template <ranges::input_range _Range, class _Allocator,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multimap(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Allocator)
+ -> unordered_multimap<__range_key_type<_Range>, __range_mapped_type<_Range>, hash<__range_key_type<_Range>>,
+ equal_to<__range_key_type<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Allocator,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multimap(from_range_t, _Range&&, _Allocator)
+ -> unordered_multimap<__range_key_type<_Range>, __range_mapped_type<_Range>, hash<__range_key_type<_Range>>,
+ equal_to<__range_key_type<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Hash, class _Allocator,
+ class = enable_if_t<!__is_allocator<_Hash>::value>,
+ class = enable_if_t<!is_integral<_Hash>::value>,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multimap(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Hash, _Allocator)
+ -> unordered_multimap<__range_key_type<_Range>, __range_mapped_type<_Range>, _Hash,
+ equal_to<__range_key_type<_Range>>, _Allocator>;
+
+#endif
+
template<class _Key, class _Tp, class _Allocator,
class = enable_if_t<__is_allocator<_Allocator>::value>>
unordered_multimap(initializer_list<pair<_Key, _Tp>>, typename allocator_traits<_Allocator>::size_type, _Allocator)
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 1bb88fc..2e2f4ca 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -58,6 +58,10 @@ public:
size_type n = 0, const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
+ template<container-compatible-range<value_type> R>
+ unordered_set(from_range_t, R&& rg, size_type n = see below,
+ const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+ const allocator_type& a = allocator_type()); // C++23
explicit unordered_set(const allocator_type&);
unordered_set(const unordered_set&);
unordered_set(const unordered_set&, const Allocator&);
@@ -77,6 +81,12 @@ public:
template <class InputIterator>
unordered_set(InputIterator f, InputIterator l, size_type n,
const hasher& hf, const allocator_type& a); // C++14
+ template<container-compatible-range<value_type> R>
+ unordered_set(from_range_t, R&& rg, size_type n, const allocator_type& a)
+ : unordered_set(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+ template<container-compatible-range<value_type> R>
+ unordered_set(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+ : unordered_set(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { } // C++23
unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a); // C++14
unordered_set(initializer_list<value_type> il, size_type n,
const hasher& hf, const allocator_type& a); // C++14
@@ -113,6 +123,8 @@ public:
iterator insert(const_iterator hint, value_type&& obj);
template <class InputIterator>
void insert(InputIterator first, InputIterator last);
+ template<container-compatible-range<value_type> R>
+ void insert_range(R&& rg); // C++23
void insert(initializer_list<value_type>);
node_type extract(const_iterator position); // C++17
@@ -191,6 +203,13 @@ unordered_set(InputIterator, InputIterator, typename see below::size_type = see
-> unordered_set<typename iterator_traits<InputIterator>::value_type,
Hash, Pred, Allocator>; // C++17
+template<ranges::input_range R,
+ class Hash = hash<ranges::range_value_t<R>>,
+ class Pred = equal_to<ranges::range_value_t<R>>,
+ class Allocator = allocator<ranges::range_value_t<R>>>
+ unordered_set(from_range_t, R&&, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+ -> unordered_set<ranges::range_value_t<R>, Hash, Pred, Allocator>; // C++23
+
template<class T, class Hash = hash<T>,
class Pred = equal_to<T>, class Allocator = allocator<T>>
unordered_set(initializer_list<T>, typename see below::size_type = see below,
@@ -211,6 +230,21 @@ unordered_set(InputIterator, InputIterator, typename see below::size_type,
equal_to<typename iterator_traits<InputIterator>::value_type>,
Allocator>; // C++17
+template<ranges::input_range R, class Allocator>
+ unordered_set(from_range_t, R&&, typename see below::size_type, Allocator)
+ -> unordered_set<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+ equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+
+template<ranges::input_range R, class Allocator>
+ unordered_set(from_range_t, R&&, Allocator)
+ -> unordered_set<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+ equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+
+template<ranges::input_range R, class Hash, class Allocator>
+ unordered_set(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+ -> unordered_set<ranges::range_value_t<R>, Hash,
+ equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+
template<class T, class Allocator>
unordered_set(initializer_list<T>, typename see below::size_type, Allocator)
-> unordered_set<T, hash<T>, equal_to<T>, Allocator>; // C++17
@@ -272,6 +306,10 @@ public:
size_type n = 0, const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
+ template<container-compatible-range<value_type> R>
+ unordered_multiset(from_range_t, R&& rg, size_type n = see below,
+ const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+ const allocator_type& a = allocator_type()); // C++23
explicit unordered_multiset(const allocator_type&);
unordered_multiset(const unordered_multiset&);
unordered_multiset(const unordered_multiset&, const Allocator&);
@@ -291,6 +329,12 @@ public:
template <class InputIterator>
unordered_multiset(InputIterator f, InputIterator l, size_type n,
const hasher& hf, const allocator_type& a); // C++14
+ template<container-compatible-range<value_type> R>
+ unordered_multiset(from_range_t, R&& rg, size_type n, const allocator_type& a)
+ : unordered_multiset(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+ template<container-compatible-range<value_type> R>
+ unordered_multiset(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+ : unordered_multiset(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { } // C++23
unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a); // C++14
unordered_multiset(initializer_list<value_type> il, size_type n,
const hasher& hf, const allocator_type& a); // C++14
@@ -327,6 +371,8 @@ public:
iterator insert(const_iterator hint, value_type&& obj);
template <class InputIterator>
void insert(InputIterator first, InputIterator last);
+ template<container-compatible-range<value_type> R>
+ void insert_range(R&& rg); // C++23
void insert(initializer_list<value_type>);
node_type extract(const_iterator position); // C++17
@@ -405,6 +451,13 @@ unordered_multiset(InputIterator, InputIterator, see below::size_type = see belo
-> unordered_multiset<typename iterator_traits<InputIterator>::value_type,
Hash, Pred, Allocator>; // C++17
+template<ranges::input_range R,
+ class Hash = hash<ranges::range_value_t<R>>,
+ class Pred = equal_to<ranges::range_value_t<R>>,
+ class Allocator = allocator<ranges::range_value_t<R>>>
+ unordered_multiset(from_range_t, R&&, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+ -> unordered_multiset<ranges::range_value_t<R>, Hash, Pred, Allocator>; // C++23
+
template<class T, class Hash = hash<T>,
class Pred = equal_to<T>, class Allocator = allocator<T>>
unordered_multiset(initializer_list<T>, typename see below::size_type = see below,
@@ -424,6 +477,21 @@ unordered_multiset(InputIterator, InputIterator, typename see below::size_type,
-> unordered_multiset<typename iterator_traits<InputIterator>::value_type, Hash,
equal_to<typename iterator_traits<InputIterator>::value_type>, Allocator>; // C++17
+template<ranges::input_range R, class Allocator>
+ unordered_multiset(from_range_t, R&&, typename see below::size_type, Allocator)
+ -> unordered_multiset<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+ equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+
+template<ranges::input_range R, class Allocator>
+ unordered_multiset(from_range_t, R&&, Allocator)
+ -> unordered_multiset<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+ equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+
+template<ranges::input_range R, class Hash, class Allocator>
+ unordered_multiset(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+ -> unordered_multiset<ranges::range_value_t<R>, Hash,
+ equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+
template<class T, class Allocator>
unordered_multiset(initializer_list<T>, typename see below::size_type, Allocator)
-> unordered_multiset<T, hash<T>, equal_to<T>, Allocator>; // C++17
@@ -469,10 +537,14 @@ template <class Value, class Hash, class Pred, class Alloc>
#include <__iterator/distance.h>
#include <__iterator/erase_if_container.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/ranges_iterator_traits.h>
#include <__memory/addressof.h>
#include <__memory/allocator.h>
#include <__memory_resource/polymorphic_allocator.h>
#include <__node_handle>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
#include <__type_traits/is_allocator.h>
#include <__utility/forward.h>
#include <version>
@@ -572,6 +644,21 @@ public:
_LIBCPP_HIDE_FROM_ABI unordered_set(_InputIterator __first, _InputIterator __last,
size_type __n, const hasher& __hf, const key_equal& __eql,
const allocator_type& __a);
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ unordered_set(from_range_t, _Range&& __range, size_type __n = /*implementation-defined*/0,
+ const hasher& __hf = hasher(), const key_equal& __eql = key_equal(),
+ const allocator_type& __a = allocator_type())
+ : __table_(__hf, __eql, __a) {
+ if (__n > 0) {
+ __table_.__rehash_unique(__n);
+ }
+ insert_range(std::forward<_Range>(__range));
+ }
+#endif
+
#if _LIBCPP_STD_VER >= 14
template <class _InputIterator>
inline _LIBCPP_INLINE_VISIBILITY
@@ -583,6 +670,19 @@ public:
size_type __n, const hasher& __hf, const allocator_type& __a)
: unordered_set(__first, __last, __n, __hf, key_equal(), __a) {}
#endif
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ unordered_set(from_range_t, _Range&& __range, size_type __n, const allocator_type& __a)
+ : unordered_set(from_range, std::forward<_Range>(__range), __n, hasher(), key_equal(), __a) {}
+
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ unordered_set(from_range_t, _Range&& __range, size_type __n, const hasher& __hf, const allocator_type& __a)
+ : unordered_set(from_range, std::forward<_Range>(__range), __n, __hf, key_equal(), __a) {}
+#endif
+
_LIBCPP_INLINE_VISIBILITY
explicit unordered_set(const allocator_type& __a);
_LIBCPP_HIDE_FROM_ABI unordered_set(const unordered_set& __u);
@@ -688,6 +788,16 @@ public:
_LIBCPP_INLINE_VISIBILITY
void insert(_InputIterator __first, _InputIterator __last);
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void insert_range(_Range&& __range) {
+ for (auto&& __element : __range) {
+ __table_.__insert_unique(std::forward<decltype(__element)>(__element));
+ }
+ }
+#endif
+
_LIBCPP_INLINE_VISIBILITY
iterator erase(const_iterator __p) {return __table_.erase(__p);}
_LIBCPP_INLINE_VISIBILITY
@@ -866,6 +976,20 @@ unordered_set(_InputIterator, _InputIterator, typename allocator_traits<_Allocat
_Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
-> unordered_set<__iter_value_type<_InputIterator>, _Hash, _Pred, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+ class _Hash = hash<ranges::range_value_t<_Range>>,
+ class _Pred = equal_to<ranges::range_value_t<_Range>>,
+ class _Allocator = allocator<ranges::range_value_t<_Range>>,
+ class = enable_if_t<!__is_allocator<_Hash>::value>,
+ class = enable_if_t<!is_integral<_Hash>::value>,
+ class = enable_if_t<!__is_allocator<_Pred>::value>,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_set(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type = 0,
+ _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
+ -> unordered_set<ranges::range_value_t<_Range>, _Hash, _Pred, _Allocator>; // C++23
+#endif
+
template<class _Tp, class _Hash = hash<_Tp>,
class _Pred = equal_to<_Tp>,
class _Allocator = allocator<_Tp>,
@@ -898,6 +1022,29 @@ unordered_set(_InputIterator, _InputIterator,
equal_to<__iter_value_type<_InputIterator>>,
_Allocator>;
+#if _LIBCPP_STD_VER >= 23
+
+template <ranges::input_range _Range, class _Allocator,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_set(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Allocator)
+ -> unordered_set<ranges::range_value_t<_Range>, hash<ranges::range_value_t<_Range>>,
+ equal_to<ranges::range_value_t<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Allocator,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_set(from_range_t, _Range&&, _Allocator)
+ -> unordered_set<ranges::range_value_t<_Range>, hash<ranges::range_value_t<_Range>>,
+ equal_to<ranges::range_value_t<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Hash, class _Allocator,
+ class = enable_if_t<!__is_allocator<_Hash>::value>,
+ class = enable_if_t<!is_integral<_Hash>::value>,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_set(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Hash, _Allocator)
+ -> unordered_set<ranges::range_value_t<_Range>, _Hash, equal_to<ranges::range_value_t<_Range>>, _Allocator>;
+
+#endif
+
template<class _Tp, class _Allocator,
class = enable_if_t<__is_allocator<_Allocator>::value>>
unordered_set(initializer_list<_Tp>, typename allocator_traits<_Allocator>::size_type, _Allocator)
@@ -1188,6 +1335,21 @@ public:
_LIBCPP_HIDE_FROM_ABI unordered_multiset(_InputIterator __first, _InputIterator __last,
size_type __n , const hasher& __hf,
const key_equal& __eql, const allocator_type& __a);
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ unordered_multiset(from_range_t, _Range&& __range, size_type __n = /*implementation-defined*/0,
+ const hasher& __hf = hasher(), const key_equal& __eql = key_equal(),
+ const allocator_type& __a = allocator_type())
+ : __table_(__hf, __eql, __a) {
+ if (__n > 0) {
+ __table_.__rehash_multi(__n);
+ }
+ insert_range(std::forward<_Range>(__range));
+ }
+#endif
+
#if _LIBCPP_STD_VER >= 14
template <class _InputIterator>
inline _LIBCPP_INLINE_VISIBILITY
@@ -1200,6 +1362,19 @@ public:
size_type __n, const hasher& __hf, const allocator_type& __a)
: unordered_multiset(__first, __last, __n, __hf, key_equal(), __a) {}
#endif
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ unordered_multiset(from_range_t, _Range&& __range, size_type __n, const allocator_type& __a)
+ : unordered_multiset(from_range, std::forward<_Range>(__range), __n, hasher(), key_equal(), __a) {}
+
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ unordered_multiset(from_range_t, _Range&& __range, size_type __n, const hasher& __hf, const allocator_type& __a)
+ : unordered_multiset(from_range, std::forward<_Range>(__range), __n, __hf, key_equal(), __a) {}
+#endif
+
_LIBCPP_INLINE_VISIBILITY
explicit unordered_multiset(const allocator_type& __a);
_LIBCPP_HIDE_FROM_ABI unordered_multiset(const unordered_multiset& __u);
@@ -1298,6 +1473,16 @@ public:
_LIBCPP_INLINE_VISIBILITY
void insert(_InputIterator __first, _InputIterator __last);
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<value_type> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void insert_range(_Range&& __range) {
+ for (auto&& __element : __range) {
+ __table_.__insert_multi(std::forward<decltype(__element)>(__element));
+ }
+ }
+#endif
+
#if _LIBCPP_STD_VER >= 17
_LIBCPP_INLINE_VISIBILITY
iterator insert(node_type&& __nh)
@@ -1477,6 +1662,20 @@ unordered_multiset(_InputIterator, _InputIterator, typename allocator_traits<_Al
_Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
-> unordered_multiset<__iter_value_type<_InputIterator>, _Hash, _Pred, _Allocator>;
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+ class _Hash = hash<ranges::range_value_t<_Range>>,
+ class _Pred = equal_to<ranges::range_value_t<_Range>>,
+ class _Allocator = allocator<ranges::range_value_t<_Range>>,
+ class = enable_if_t<!__is_allocator<_Hash>::value>,
+ class = enable_if_t<!is_integral<_Hash>::value>,
+ class = enable_if_t<!__is_allocator<_Pred>::value>,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multiset(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type = 0,
+ _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
+ -> unordered_multiset<ranges::range_value_t<_Range>, _Hash, _Pred, _Allocator>; // C++23
+#endif
+
template<class _Tp, class _Hash = hash<_Tp>,
class _Pred = equal_to<_Tp>, class _Allocator = allocator<_Tp>,
class = enable_if_t<!__is_allocator<_Hash>::value>,
@@ -1507,6 +1706,29 @@ unordered_multiset(_InputIterator, _InputIterator, typename allocator_traits<_Al
equal_to<__iter_value_type<_InputIterator>>,
_Allocator>;
+#if _LIBCPP_STD_VER >= 23
+
+template <ranges::input_range _Range, class _Allocator,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multiset(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Allocator)
+ -> unordered_multiset<ranges::range_value_t<_Range>, hash<ranges::range_value_t<_Range>>,
+ equal_to<ranges::range_value_t<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Allocator,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multiset(from_range_t, _Range&&, _Allocator)
+ -> unordered_multiset<ranges::range_value_t<_Range>, hash<ranges::range_value_t<_Range>>,
+ equal_to<ranges::range_value_t<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Hash, class _Allocator,
+ class = enable_if_t<!__is_allocator<_Hash>::value>,
+ class = enable_if_t<!is_integral<_Hash>::value>,
+ class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multiset(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Hash, _Allocator)
+ -> unordered_multiset<ranges::range_value_t<_Range>, _Hash, equal_to<ranges::range_value_t<_Range>>, _Allocator>;
+
+#endif
+
template<class _Tp, class _Allocator,
class = enable_if_t<__is_allocator<_Allocator>::value>>
unordered_multiset(initializer_list<_Tp>, typename allocator_traits<_Allocator>::size_type, _Allocator)
diff --git a/libcxx/test/std/containers/associative/from_range_associative_containers.h b/libcxx/test/std/containers/associative/from_range_associative_containers.h
new file mode 100644
index 0000000..6d2d6e6
--- /dev/null
+++ b/libcxx/test/std/containers/associative/from_range_associative_containers.h
@@ -0,0 +1,304 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUPPORT_FROM_RANGE_ASSOCIATIVE_CONTAINERS_H
+#define SUPPORT_FROM_RANGE_ASSOCIATIVE_CONTAINERS_H
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <ranges>
+#include <vector>
+#include <utility>
+
+#include "../exception_safety_helpers.h"
+#include "../from_range_helpers.h"
+#include "../test_compare.h"
+#include "MoveOnly.h"
+#include "almost_satisfies_types.h"
+#include "count_new.h"
+#include "test_macros.h"
+
+template <class Container, class Range>
+concept HasFromRangeCtr = requires (Range&& range) {
+ Container(std::from_range, std::forward<Range>(range));
+ Container(std::from_range, std::forward<Range>(range), std::less<typename Container::key_type>());
+ Container(std::from_range, std::forward<Range>(range), std::less<typename Container::key_type>(),
+ std::allocator<typename Container::value_type>());
+ Container(std::from_range, std::forward<Range>(range), std::allocator<typename Container::value_type>());
+};
+
+template <template <class...> class Container, class K, class V, class K2, class V2>
+constexpr bool test_map_constraints() {
+ using ValueType = std::pair<const K, V>;
+
+ // Input range with the same value type.
+ static_assert(HasFromRangeCtr<Container<K, V>, InputRange<ValueType>>);
+ // Input range with a convertible value type.
+ static_assert(HasFromRangeCtr<Container<K, V>, InputRange<std::pair<const K2, V2>>>);
+ // Input range with a non-convertible value type.
+ static_assert(!HasFromRangeCtr<Container<K, V>, InputRange<std::pair<const Empty, V>>>);
+ static_assert(!HasFromRangeCtr<Container<K, V>, InputRange<std::pair<const K, Empty>>>);
+ // Not an input range.
+ static_assert(!HasFromRangeCtr<Container<K, V>, InputRangeNotDerivedFromGeneric<ValueType>>);
+
+ return true;
+}
+
+template <template <class ...> class Container,
+ class K,
+ class V,
+ class Iter,
+ class Sent,
+ class Comp,
+ class Alloc,
+ class ValueType = std::pair<const K, V>>
+void test_associative_map_with_input(std::vector<ValueType>&& input) {
+ auto in = wrap_input<Iter, Sent>(input);
+
+ { // (range)
+ Container<K, V> c(std::from_range, in);
+
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ }
+
+ { // (range, comp)
+ Comp comp;
+ Container<K, V, Comp> c(std::from_range, in, comp);
+
+ assert(c.key_comp() == comp);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ }
+
+ { // (range, allocator)
+ Alloc alloc;
+ Container<K, V, std::less<K>, Alloc> c(std::from_range, in, alloc);
+
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ }
+
+ { // (range, comp, allocator)
+ Comp comp;
+ Alloc alloc;
+ Container<K, V, Comp, Alloc> c(std::from_range, in, comp, alloc);
+
+ assert(c.key_comp() == comp);
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ }
+}
+
+template <template <class ...> class Container,
+ class K,
+ class V,
+ class Iter,
+ class Sent,
+ class Comp,
+ class Alloc>
+void test_associative_map() {
+ auto test_with_input = &test_associative_map_with_input<Container, K, V, Iter, Sent, Comp, Alloc>;
+
+ // Normal input.
+ test_with_input({{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}});
+ // Empty input.
+ test_with_input({});
+ // Single-element input.
+ test_with_input({{1, 2}});
+}
+
+template <template <class ...> class Container>
+void test_associative_map_move_only() {
+ std::pair<const int, MoveOnly> input[5];
+ std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+ [[maybe_unused]] Container<int, MoveOnly> c(std::from_range, in);
+}
+
+template <template <class ...> class Container>
+void test_map_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using K = int;
+ using V = ThrowingCopy<3>;
+
+ V::throwing_enabled = false;
+ std::pair<const K, V> in[5] = {
+ {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}
+ };
+ V::throwing_enabled = true;
+ V::reset();
+
+ try {
+ Container<K, V> c(std::from_range, in);
+ assert(false); // The constructor call above should throw.
+
+ } catch (int) {
+ assert(V::created_by_copying == 3);
+ assert(V::destroyed == 2); // No destructor call for the partially-constructed element.
+ }
+#endif
+}
+
+template <template <class ...> class Container, class K, class V>
+void test_map_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using ValueType = std::pair<const K, V>;
+ ValueType in[] = {
+ ValueType{K{1}, V{1}}
+ };
+
+ try {
+ ThrowingAllocator<ValueType> alloc;
+
+ globalMemCounter.reset();
+ Container<K, V, test_less<K>, ThrowingAllocator<ValueType>> c(std::from_range, in, alloc);
+ assert(false); // The constructor call above should throw.
+
+ } catch (int) {
+ assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+ }
+#endif
+}
+
+template <class Container, class Range>
+concept SetHasFromRangeCtr = requires (Range&& range) {
+ Container(std::from_range, std::forward<Range>(range));
+ Container(std::from_range, std::forward<Range>(range), std::less<typename Container::value_type>());
+ Container(std::from_range, std::forward<Range>(range), std::less<typename Container::value_type>(),
+ std::allocator<typename Container::value_type>());
+ Container(std::from_range, std::forward<Range>(range), std::allocator<typename Container::value_type>());
+};
+
+template <template <class...> class Container, class T, class U>
+constexpr bool test_set_constraints() {
+ // Input range with the same value type.
+ static_assert(SetHasFromRangeCtr<Container<T>, InputRange<T>>);
+ // Input range with a convertible value type.
+ static_assert(SetHasFromRangeCtr<Container<T>, InputRange<U>>);
+ // Input range with a non-convertible value type.
+ static_assert(!SetHasFromRangeCtr<Container<T>, InputRange<Empty>>);
+ // Not an input range.
+ static_assert(!SetHasFromRangeCtr<Container<T>, InputRangeNotDerivedFromGeneric<T>>);
+
+ return true;
+}
+
+template <template <class ...> class Container,
+ class T,
+ class Iter,
+ class Sent,
+ class Comp,
+ class Alloc>
+void test_associative_set_with_input(std::vector<T>&& input) {
+ auto b = Iter(input.data());
+ auto e = Iter(input.data() + input.size());
+ std::ranges::subrange in(std::move(b), Sent(std::move(e)));
+
+ { // (range)
+ Container<T> c(std::from_range, in);
+
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ }
+
+ { // (range, comp)
+ Comp comp;
+ Container<T, Comp> c(std::from_range, in, comp);
+
+ assert(c.key_comp() == comp);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ }
+
+ { // (range, allocator)
+ Alloc alloc;
+ Container<T, std::less<T>, Alloc> c(std::from_range, in, alloc);
+
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ }
+
+ { // (range, comp, allocator)
+ Comp comp;
+ Alloc alloc;
+ Container<T, Comp, Alloc> c(std::from_range, in, comp, alloc);
+
+ assert(c.key_comp() == comp);
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ }
+}
+
+template <template <class ...> class Container,
+ class T,
+ class Iter,
+ class Sent,
+ class Comp,
+ class Alloc>
+void test_associative_set() {
+ auto test_with_input = &test_associative_set_with_input<Container, T, Iter, Sent, Comp, Alloc>;
+
+ // Normal input.
+ test_with_input({0, 5, 12, 7, -1, 8, 26});
+ // Empty input.
+ test_with_input({});
+ // Single-element input.
+ test_with_input({5});
+}
+
+template <template <class ...> class Container>
+void test_associative_set_move_only() {
+ MoveOnly input[5];
+ std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+ [[maybe_unused]] Container<MoveOnly> c(std::from_range, in);
+}
+
+template <template <class ...> class Container>
+void test_set_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using T = ThrowingCopy<3>;
+ T::reset();
+ T in[5] = {{1}, {2}, {3}, {4}, {5}};
+
+ try {
+ Container<T> c(std::from_range, in);
+ assert(false); // The constructor call above should throw.
+
+ } catch (int) {
+ assert(T::created_by_copying == 3);
+ assert(T::destroyed == 2); // No destructor call for the partially-constructed element.
+ }
+#endif
+}
+
+template <template <class ...> class Container, class T>
+void test_set_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ T in[] = {1, 2};
+
+ try {
+ ThrowingAllocator<T> alloc;
+
+ globalMemCounter.reset();
+ Container<T, test_less<T>, ThrowingAllocator<T>> c(std::from_range, in, alloc);
+ assert(false); // The constructor call above should throw.
+
+ } catch (int) {
+ assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+ }
+#endif
+}
+
+#endif // SUPPORT_FROM_RANGE_ASSOCIATIVE_CONTAINERS_H
diff --git a/libcxx/test/std/containers/associative/map/map.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/map/map.cons/deduct.pass.cpp
index f104397..d68e6af 100644
--- a/libcxx/test/std/containers/associative/map/map.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.cons/deduct.pass.cpp
@@ -24,8 +24,18 @@
// template<class Key, class Allocator>
// map(initializer_list<Key>, Allocator)
// -> map<Key, less<Key>, Allocator>;
+//
+// template<ranges::input_range R, class Compare = less<range-key-type<R>,
+// class Allocator = allocator<range-to-alloc-type<R>>>
+// map(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+// -> map<range-key-type<R>, range-mapped-type<R>, Compare, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+// map(from_range_t, R&&, Allocator)
+// -> map<range-key-type<R>, range-mapped-type<R>, less<range-key-type<R>>, Allocator>; // C++23
#include <algorithm> // std::equal
+#include <array>
#include <cassert>
#include <climits> // INT_MAX
#include <functional>
@@ -152,6 +162,35 @@ int main(int, char**)
ASSERT_SAME_TYPE(decltype(m2), std::map<int, int>);
}
+#if TEST_STD_VER >= 23
+ {
+ using Range = std::array<P, 0>;
+ using Comp = std::greater<int>;
+ using DefaultComp = std::less<int>;
+ using Alloc = test_allocator<PC>;
+
+ { // (from_range, range)
+ std::map c(std::from_range, Range());
+ static_assert(std::is_same_v<decltype(c), std::map<int, long>>);
+ }
+
+ { // (from_range, range, comp)
+ std::map c(std::from_range, Range(), Comp());
+ static_assert(std::is_same_v<decltype(c), std::map<int, long, Comp>>);
+ }
+
+ { // (from_range, range, comp, alloc)
+ std::map c(std::from_range, Range(), Comp(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::map<int, long, Comp, Alloc>>);
+ }
+
+ { // (from_range, range, alloc)
+ std::map c(std::from_range, Range(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::map<int, long, DefaultComp, Alloc>>);
+ }
+ }
+#endif
+
AssociativeContainerDeductionGuidesSfinaeAway<std::map, std::map<int, long>>();
return 0;
diff --git a/libcxx/test/std/containers/associative/map/map.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/map/map.cons/from_range.pass.cpp
new file mode 100644
index 0000000..f375dbf
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.cons/from_range.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<value_type> R>
+// map(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+// map(from_range_t, R&& rg, const Allocator& a))
+// : map(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+
+#include <array>
+#include <map>
+
+#include "../../from_range_associative_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+ using T = std::pair<const int, char>;
+
+ std::array input = {
+ T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}
+ };
+ std::array expected = {
+ T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{4, 'a'}
+ };
+ auto c = std::map<int, char>(std::from_range, input);
+ assert(std::ranges::is_permutation(expected, c));
+}
+
+int main(int, char**) {
+ using T = std::pair<const int, int>;
+ for_all_iterators_and_allocators<T>([]<class Iter, class Sent, class Alloc>() {
+ test_associative_map<std::map, int, int, Iter, Sent, test_less<int>, Alloc>();
+ });
+ test_associative_map_move_only<std::map>();
+ test_duplicates();
+
+ static_assert(test_map_constraints<std::map, int, int, double, double>());
+
+ test_map_exception_safety_throwing_copy<std::map>();
+ test_map_exception_safety_throwing_allocator<std::map, int, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/insert_range.pass.cpp
new file mode 100644
index 0000000..1d7fe71
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/insert_range.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
+
+// <map>
+
+// template<container-compatible-range<value_type> R>
+// void insert_range(R&& rg); // C++23
+
+#include <map>
+
+#include "../../../insert_range_maps_sets.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+ // Note: we want to use a pair with non-const elements for input (an assignable type is a lot more convenient) but
+ // have to use the exact `value_type` of the map (that is, `pair<const K, V>`) for the allocator.
+ using Pair = std::pair<int, char>;
+ using ConstPair = std::pair<const int, char>;
+ for_all_iterators_and_allocators<ConstPair, const Pair*>([]<class Iter, class Sent, class Alloc>() {
+ test_map_set_insert_range<std::map<int, char, test_less<int>, Alloc>, Pair, Iter, Sent>();
+ });
+
+ static_assert(test_map_constraints_insert_range<std::map, int, int, char, double>());
+
+ test_map_insert_range_move_only<std::map>();
+
+ test_map_insert_range_exception_safety_throwing_copy<std::map>();
+ test_assoc_map_insert_range_exception_safety_throwing_allocator<std::map, int, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp
index c71a739..56bc540 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp
@@ -24,8 +24,18 @@
// template<class Key, class Allocator>
// multimap(initializer_list<Key>, Allocator)
// -> multimap<Key, less<Key>, Allocator>;
+//
+// template<ranges::input_range R, class Compare = less<range-key-type<R>>,
+// class Allocator = allocator<range-to-alloc-type<R>>>
+// multimap(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+// -> multimap<range-key-type<R>, range-mapped-type<R>, Compare, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+// multimap(from_range_t, R&&, Allocator)
+// -> multimap<range-key-type<R>, range-mapped-type<R>, less<range-key-type<R>>, Allocator>; // C++23
#include <algorithm> // std::equal
+#include <array>
#include <cassert>
#include <climits> // INT_MAX
#include <functional>
@@ -152,6 +162,35 @@ int main(int, char**)
ASSERT_SAME_TYPE(decltype(m2), std::multimap<int, int>);
}
+#if TEST_STD_VER >= 23
+ {
+ using Range = std::array<P, 0>;
+ using Comp = std::greater<int>;
+ using DefaultComp = std::less<int>;
+ using Alloc = test_allocator<PC>;
+
+ { // (from_range, range)
+ std::multimap c(std::from_range, Range());
+ static_assert(std::is_same_v<decltype(c), std::multimap<int, long>>);
+ }
+
+ { // (from_range, range, comp)
+ std::multimap c(std::from_range, Range(), Comp());
+ static_assert(std::is_same_v<decltype(c), std::multimap<int, long, Comp>>);
+ }
+
+ { // (from_range, range, comp, alloc)
+ std::multimap c(std::from_range, Range(), Comp(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::multimap<int, long, Comp, Alloc>>);
+ }
+
+ { // (from_range, range, alloc)
+ std::multimap c(std::from_range, Range(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::multimap<int, long, DefaultComp, Alloc>>);
+ }
+ }
+#endif
+
AssociativeContainerDeductionGuidesSfinaeAway<std::multimap, std::multimap<int, long>>();
return 0;
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.cons/from_range.pass.cpp
new file mode 100644
index 0000000..b38078c
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.cons/from_range.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<value_type> R>
+// multimap(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+// multimap(from_range_t, R&& rg, const Allocator& a))
+// : multimap(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+
+#include <array>
+#include <map>
+
+#include "../../from_range_associative_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+ using T = std::pair<const int, char>;
+ std::array input = {
+ T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}
+ };
+ auto c = std::multimap<int, char>(std::from_range, input);
+ assert(std::ranges::is_permutation(input, c));
+}
+
+int main(int, char**) {
+ using T = std::pair<const int, int>;
+ for_all_iterators_and_allocators<T>([]<class Iter, class Sent, class Alloc>() {
+ test_associative_map<std::multimap, int, int, Iter, Sent, test_less<int>, Alloc>();
+ });
+ test_associative_map_move_only<std::multimap>();
+ test_duplicates();
+
+ static_assert(test_map_constraints<std::multimap, int, int, double, double>());
+
+ test_map_exception_safety_throwing_copy<std::multimap>();
+ test_map_exception_safety_throwing_allocator<std::multimap, int, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/insert_range.pass.cpp
new file mode 100644
index 0000000..c7c05a8
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/insert_range.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
+
+// <map>
+
+// template<container-compatible-range<value_type> R>
+// void insert_range(R&& rg); // C++23
+
+#include <map>
+
+#include "../../../insert_range_maps_sets.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+ // Note: we want to use a pair with non-const elements for input (an assignable type is a lot more convenient) but
+ // have to use the exact `value_type` of the map (that is, `pair<const K, V>`) for the allocator.
+ using Pair = std::pair<int, char>;
+ using ConstPair = std::pair<const int, char>;
+ for_all_iterators_and_allocators<ConstPair, const Pair*>([]<class Iter, class Sent, class Alloc>() {
+ test_map_set_insert_range<std::multimap<int, char, test_less<int>, Alloc>, Pair, Iter, Sent>(/*allow_duplicates=*/true);
+ });
+
+ static_assert(test_map_constraints_insert_range<std::multimap, int, int, char, double>());
+
+ test_map_insert_range_move_only<std::multimap>();
+
+ test_map_insert_range_exception_safety_throwing_copy<std::multimap>();
+ test_assoc_map_insert_range_exception_safety_throwing_allocator<std::multimap, int, int>();
+
+ return 0;
+}
+
diff --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
new file mode 100644
index 0000000..9dd85ee
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
+
+// <set>
+
+// template<container-compatible-range<value_type> R>
+// void insert_range(R&& rg); // C++23
+
+#include <set>
+
+#include "../../insert_range_maps_sets.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_map_set_insert_range<std::multiset<int, test_less<int>, Alloc>, int, Iter, Sent>(/*allow_duplicates=*/true);
+ });
+
+ static_assert(test_set_constraints_insert_range<std::multiset, int, double>());
+
+ test_set_insert_range_move_only<std::multiset>();
+
+ test_set_insert_range_exception_safety_throwing_copy<std::multiset>();
+ test_assoc_set_insert_range_exception_safety_throwing_allocator<std::multiset, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
index 233ddd2..93bff1a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
@@ -27,8 +27,18 @@
// template<class Key, class Allocator>
// multiset(initializer_list<Key>, Allocator)
// -> multiset<Key, less<Key>, Allocator>;
+//
+// template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>,
+// class Allocator = allocator<ranges::range_value_t<R>>>
+// multiset(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+// -> multiset<ranges::range_value_t<R>, Compare, Allocator>;
+//
+// template<ranges::input_range R, class Allocator>
+// multiset(from_range_t, R&&, Allocator)
+// -> multiset<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>;
#include <algorithm> // std::equal
+#include <array>
#include <cassert>
#include <climits> // INT_MAX
#include <functional>
@@ -187,6 +197,35 @@ int main(int, char **) {
assert(s.size() == 2);
}
+#if TEST_STD_VER >= 23
+ {
+ using Range = std::array<int, 0>;
+ using Comp = std::greater<int>;
+ using DefaultComp = std::less<int>;
+ using Alloc = test_allocator<int>;
+
+ { // (from_range, range)
+ std::multiset c(std::from_range, Range());
+ static_assert(std::is_same_v<decltype(c), std::multiset<int>>);
+ }
+
+ { // (from_range, range, comp)
+ std::multiset c(std::from_range, Range(), Comp());
+ static_assert(std::is_same_v<decltype(c), std::multiset<int, Comp>>);
+ }
+
+ { // (from_range, range, comp, alloc)
+ std::multiset c(std::from_range, Range(), Comp(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::multiset<int, Comp, Alloc>>);
+ }
+
+ { // (from_range, range, alloc)
+ std::multiset c(std::from_range, Range(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::multiset<int, DefaultComp, Alloc>>);
+ }
+ }
+#endif
+
AssociativeContainerDeductionGuidesSfinaeAway<std::multiset, std::multiset<int>>();
return 0;
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
new file mode 100644
index 0000000..74533d7
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<value_type> R>
+// multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+// multiset(from_range_t, R&& rg, const Allocator& a))
+// : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+
+#include <algorithm>
+#include <array>
+#include <set>
+
+#include "../../from_range_associative_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+ std::array input = {1, 2, 3, 3, 3, 4, 2, 1, 2};
+ auto c = std::multiset<int>(std::from_range, input);
+ assert(std::ranges::is_permutation(input, c));
+}
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+ test_associative_set<std::multiset, int, Iter, Sent, test_less<int>, Alloc>();
+ });
+ test_associative_set_move_only<std::multiset>();
+ test_duplicates();
+
+ static_assert(test_set_constraints<std::multiset, int, double>());
+
+ test_set_exception_safety_throwing_copy<std::multiset>();
+ test_set_exception_safety_throwing_allocator<std::multiset, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
new file mode 100644
index 0000000..1956fc6
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
+
+// <set>
+
+// template<container-compatible-range<value_type> R>
+// void insert_range(R&& rg); // C++23
+
+#include <set>
+
+#include "../../insert_range_maps_sets.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_map_set_insert_range<std::set<int, test_less<int>, Alloc>, int, Iter, Sent>();
+ });
+
+ static_assert(test_set_constraints_insert_range<std::set, int, double>());
+
+ test_set_insert_range_move_only<std::set>();
+
+ test_set_insert_range_exception_safety_throwing_copy<std::set>();
+ test_assoc_set_insert_range_exception_safety_throwing_allocator<std::set, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
index fdc7255..74e72b7 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
@@ -27,8 +27,18 @@
// template<class Key, class Allocator>
// set(initializer_list<Key>, Allocator)
// -> set<Key, less<Key>, Allocator>;
+//
+// template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>,
+// class Allocator = allocator<ranges::range_value_t<R>>>
+// set(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+// -> set<ranges::range_value_t<R>, Compare, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+// set(from_range_t, R&&, Allocator)
+// -> set<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>; // C++23
#include <algorithm> // std::equal
+#include <array>
#include <cassert>
#include <climits> // INT_MAX
#include <functional>
@@ -185,6 +195,35 @@ int main(int, char **) {
assert(s.size() == 2);
}
+#if TEST_STD_VER >= 23
+ {
+ using Range = std::array<int, 0>;
+ using Comp = std::greater<int>;
+ using DefaultComp = std::less<int>;
+ using Alloc = test_allocator<int>;
+
+ { // (from_range, range)
+ std::set c(std::from_range, Range());
+ static_assert(std::is_same_v<decltype(c), std::set<int>>);
+ }
+
+ { // (from_range, range, comp)
+ std::set c(std::from_range, Range(), Comp());
+ static_assert(std::is_same_v<decltype(c), std::set<int, Comp>>);
+ }
+
+ { // (from_range, range, comp, alloc)
+ std::set c(std::from_range, Range(), Comp(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::set<int, Comp, Alloc>>);
+ }
+
+ { // (from_range, range, alloc)
+ std::set c(std::from_range, Range(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::set<int, DefaultComp, Alloc>>);
+ }
+ }
+#endif
+
AssociativeContainerDeductionGuidesSfinaeAway<std::set, std::set<int>>();
return 0;
diff --git a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
new file mode 100644
index 0000000..4c9b4aa
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<value_type> R>
+// set(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+// set(from_range_t, R&& rg, const Allocator& a))
+// : set(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+
+#include <array>
+#include <set>
+
+#include "../../from_range_associative_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+ using T = KeyValue;
+
+ std::array input = {
+ T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}
+ };
+ std::array expected = {
+ T{1, 'a'}, T{2, 'b'}, T{3, 'c'}, T{4, 'a'}
+ };
+ auto c = std::set<T>(std::from_range, input);
+ assert(std::ranges::is_permutation(expected, c));
+}
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+ test_associative_set<std::set, int, Iter, Sent, test_less<int>, Alloc>();
+ });
+ test_associative_set_move_only<std::set>();
+ test_duplicates();
+
+ static_assert(test_set_constraints<std::set, int, double>());
+
+ test_set_exception_safety_throwing_copy<std::set>();
+ test_set_exception_safety_throwing_allocator<std::set, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/insert_range_helpers.h b/libcxx/test/std/containers/insert_range_helpers.h
index 2c4e14c..531a7df 100644
--- a/libcxx/test/std/containers/insert_range_helpers.h
+++ b/libcxx/test/std/containers/insert_range_helpers.h
@@ -21,6 +21,7 @@
#include <unordered_set>
#include <vector>
+#include "exception_safety_helpers.h"
#include "from_range_helpers.h"
#include "min_allocator.h"
#include "test_allocator.h"
diff --git a/libcxx/test/std/containers/insert_range_maps_sets.h b/libcxx/test/std/containers/insert_range_maps_sets.h
new file mode 100644
index 0000000..82fea93
--- /dev/null
+++ b/libcxx/test/std/containers/insert_range_maps_sets.h
@@ -0,0 +1,415 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUPPORT_INSERT_RANGE_MAPS_SETS_H
+#define SUPPORT_INSERT_RANGE_MAPS_SETS_H
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <type_traits>
+#include <vector>
+
+#include "MoveOnly.h"
+#include "almost_satisfies_types.h"
+#include "count_new.h"
+#include "exception_safety_helpers.h"
+#include "insert_range_helpers.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_compare.h"
+#include "test_hash.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+template <class Container, class Range>
+concept HasInsertRange = requires (Container& c, Range&& range) {
+ c.insert_range(range);
+};
+
+template <template <class...> class Container, class T, class U>
+constexpr bool test_set_constraints_insert_range() {
+ // Input range with the same value type.
+ static_assert(HasInsertRange<Container<T>, InputRange<T>>);
+ // Input range with a convertible value type.
+ static_assert(HasInsertRange<Container<T>, InputRange<U>>);
+ // Input range with a non-convertible value type.
+ static_assert(!HasInsertRange<Container<T>, InputRange<Empty>>);
+ // Not an input range.
+ static_assert(!HasInsertRange<Container<T>, InputRangeNotDerivedFrom>);
+ static_assert(!HasInsertRange<Container<T>, InputRangeNotIndirectlyReadable>);
+ static_assert(!HasInsertRange<Container<T>, InputRangeNotInputOrOutputIterator>);
+
+ return true;
+}
+
+template <template <class...> class Container, class K, class V, class K2, class V2>
+constexpr bool test_map_constraints_insert_range() {
+ using ValueType = std::pair<const K, V>;
+
+ // Input range with the same value type.
+ static_assert(HasInsertRange<Container<K, V>, InputRange<ValueType>>);
+ // Input range with a convertible value type.
+ static_assert(HasInsertRange<Container<K, V>, InputRange<std::pair<const K2, V2>>>);
+ // Input range with a non-convertible value type.
+ static_assert(!HasInsertRange<Container<K, V>, InputRange<std::pair<const K, Empty>>>);
+ static_assert(!HasInsertRange<Container<K, V>, InputRange<std::pair<const Empty, V>>>);
+ // Not an input range.
+ static_assert(!HasInsertRange<Container<K, V>, InputRangeNotDerivedFromGeneric<ValueType>>);
+
+ return true;
+}
+
+template <class T>
+struct TestCaseMapSet {
+ Buffer<T> initial;
+ Buffer<T> input;
+ Buffer<T> expected;
+ Buffer<T> expected_multi;
+};
+
+// Empty container.
+
+template <class T>
+TestCaseMapSet<T> constexpr EmptyContainer_EmptyRange {
+ .initial = {}, .input = {}, .expected = {}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr EmptyContainer_OneElementRange {
+ .initial = {}, .input = {1}, .expected = {1}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr EmptyContainer_OneElementRange<std::pair<K, V>> {
+ .initial = {}, .input = {{1, 'a'}}, .expected = {{1, 'a'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr EmptyContainer_RangeNoDuplicates {
+ .initial = {}, .input = {5, 1, 3, 8, 6}, .expected = {5, 1, 3, 8, 6}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr EmptyContainer_RangeNoDuplicates<std::pair<K, V>> {
+ .initial = {}, .input = {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}},
+ .expected = {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr EmptyContainer_RangeWithDuplicates {
+ .initial = {},
+ .input = {5, 1, 1, 3, 5, 8, 5, 6, 10},
+ .expected = {5, 1, 3, 8, 6, 10},
+ .expected_multi = {5, 1, 1, 3, 5, 8, 5, 6, 10}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr EmptyContainer_RangeWithDuplicates<std::pair<K, V>> {
+ .initial = {},
+ .input = {{5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'}},
+ .expected = {{5, 'a'}, {1, 'a'}, {3, 'a'}, {8, 'a'}, {6, 'a'}, {10, 'b'}},
+ .expected_multi = {{5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'}}
+};
+
+// One-element container.
+
+template <class T>
+TestCaseMapSet<T> constexpr OneElementContainer_EmptyRange {
+ .initial = {10}, .input = {}, .expected = {10}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr OneElementContainer_EmptyRange<std::pair<K, V>> {
+ .initial = {{10, 'A'}}, .input = {}, .expected = {{10, 'A'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr OneElementContainer_OneElementRange {
+ .initial = {10}, .input = {1}, .expected = {1, 10}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr OneElementContainer_OneElementRange<std::pair<K, V>> {
+ .initial = {{10, 'A'}}, .input = {{1, 'a'}}, .expected = {{1, 'a'}, {10, 'A'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr OneElementContainer_RangeNoDuplicates {
+ .initial = {10}, .input = {5, 1, 3, 8, 6}, .expected = {5, 1, 3, 8, 6, 10}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr OneElementContainer_RangeNoDuplicates<std::pair<K, V>> {
+ .initial = {{10, 'A'}}, .input = {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}},
+ .expected = {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}, {10, 'A'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr OneElementContainer_RangeWithDuplicates {
+ .initial = {10},
+ .input = {5, 1, 1, 3, 5, 8, 5, 6, 10},
+ .expected = {5, 1, 3, 8, 6, 10},
+ .expected_multi = {5, 1, 1, 3, 5, 8, 5, 6, 10, 10}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr OneElementContainer_RangeWithDuplicates<std::pair<K, V>> {
+ .initial = {{10, 'A'}},
+ .input = {{5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'}},
+ .expected = {{5, 'a'}, {1, 'a'}, {3, 'a'}, {8, 'a'}, {6, 'a'}, {10, 'A'}},
+ .expected_multi = {
+ {5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'A'}, {10, 'b'}
+ }
+};
+
+// N-elements container.
+
+template <class T>
+TestCaseMapSet<T> constexpr NElementsContainer_EmptyRange {
+ .initial = {10, 15, 19, 16}, .input = {}, .expected = {10, 15, 19, 16}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr NElementsContainer_EmptyRange<std::pair<K, V>> {
+ .initial = {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}, .input = {},
+ .expected = {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr NElementsContainer_OneElementRange {
+ .initial = {10, 15, 19, 16}, .input = {1}, .expected = {1, 10, 15, 19, 16}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr NElementsContainer_OneElementRange<std::pair<K, V>> {
+ .initial = {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}, .input = {{1, 'a'}},
+ .expected = {{1, 'a'}, {10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr NElementsContainer_RangeNoDuplicates {
+ .initial = {10, 15, 19, 16}, .input = {5, 1, 3, 8, 6}, .expected = {5, 1, 3, 8, 6, 10, 15, 19, 16}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr NElementsContainer_RangeNoDuplicates<std::pair<K, V>> {
+ .initial = {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}},
+ .input = {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}},
+ .expected = {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}, {10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr NElementsContainer_RangeWithDuplicates {
+ .initial = {10, 15, 19, 16},
+ .input = {5, 1, 1, 3, 5, 8, 5, 6, 10},
+ .expected = {5, 1, 3, 8, 6, 10, 15, 19, 16},
+ .expected_multi = {5, 1, 1, 3, 5, 8, 5, 6, 10, 10, 15, 19, 16}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr NElementsContainer_RangeWithDuplicates<std::pair<K, V>> {
+ .initial = {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}},
+ .input = {{5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'}},
+ .expected = {{5, 'a'}, {1, 'a'}, {3, 'a'}, {8, 'a'}, {6, 'a'}, {10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}},
+ .expected_multi = {
+ {5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'},
+ {10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}
+ }
+};
+
+template <class Container, class T, class Iter, class Sent>
+void test_map_set_insert_range(bool allow_duplicates = false) {
+ auto test = [&](const TestCaseMapSet<T>& test_case, bool check_multi = false) {
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+
+ c.insert_range(in);
+ if (check_multi) {
+ return std::ranges::is_permutation(c, test_case.expected_multi);
+ } else {
+ return std::ranges::is_permutation(c, test_case.expected);
+ }
+ };
+
+ { // Empty container.
+ // empty_c.insert_range(empty_range)
+ assert(test(EmptyContainer_EmptyRange<T>));
+ // empty_c.insert_range(one_element_range)
+ assert(test(EmptyContainer_OneElementRange<T>));
+ // empty_c.insert_range(range_no_duplicates)
+ assert(test(EmptyContainer_RangeNoDuplicates<T>));
+ // empty_c.insert_range(range_with_duplicates)
+ assert(test(EmptyContainer_RangeWithDuplicates<T>, allow_duplicates));
+ }
+
+ { // One-element container.
+ // one_element_c.insert_range(empty_range)
+ assert(test(OneElementContainer_EmptyRange<T>));
+ // one_element_c.insert_range(one_element_range)
+ assert(test(OneElementContainer_OneElementRange<T>));
+ // one_element_c.insert_range(range_no_duplicates)
+ assert(test(OneElementContainer_RangeNoDuplicates<T>));
+ // one_element_c.insert_range(range_with_duplicates)
+ assert(test(OneElementContainer_RangeWithDuplicates<T>, allow_duplicates));
+ }
+
+ { // N-elements container.
+ // n_elements_c.insert_range(empty_range)
+ assert(test(NElementsContainer_EmptyRange<T>));
+ // n_elements_c.insert_range(one_element_range)
+ assert(test(NElementsContainer_OneElementRange<T>));
+ // n_elements_c.insert_range(range_no_duplicates)
+ assert(test(NElementsContainer_RangeNoDuplicates<T>));
+ // n_elements_c.insert_range(range_with_duplicates)
+ assert(test(NElementsContainer_RangeWithDuplicates<T>, allow_duplicates));
+ }
+}
+
+// Move-only types.
+
+template <template <class ...> class Container>
+void test_set_insert_range_move_only() {
+ MoveOnly input[5];
+ std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+ Container<MoveOnly> c;
+ c.insert_range(in);
+}
+
+template <template <class ...> class Container>
+void test_map_insert_range_move_only() {
+ using Value = std::pair<const int, MoveOnly>;
+ Value input[5];
+ std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+ Container<int, MoveOnly> c;
+ c.insert_range(in);
+}
+
+// Exception safety.
+
+template <template <class ...> class Container>
+void test_set_insert_range_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using T = ThrowingCopy<3>;
+ T::reset();
+ T in[5] = {{1}, {2}, {3}, {4}, {5}};
+
+ try {
+ Container<T> c;
+ c.insert_range(in);
+ assert(false); // The constructor call above should throw.
+
+ } catch (int) {
+ assert(T::created_by_copying == 3);
+ assert(T::destroyed == 2); // No destructor call for the partially-constructed element.
+ }
+#endif
+}
+
+template <template <class ...> class Container>
+void test_map_insert_range_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using K = int;
+ using V = ThrowingCopy<3>;
+
+ V::throwing_enabled = false;
+ std::pair<const K, V> in[5] = {
+ {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}
+ };
+ V::throwing_enabled = true;
+ V::reset();
+
+ try {
+ Container<K, V> c;
+ c.insert_range(in);
+ assert(false); // The function call above should throw.
+
+ } catch (int) {
+ assert(V::created_by_copying == 3);
+ assert(V::destroyed == 2); // No destructor call for the partially-constructed element.
+ }
+#endif
+}
+
+template <template <class ...> class Container, class T>
+void test_assoc_set_insert_range_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ T in[] = {1, 2};
+
+ try {
+ ThrowingAllocator<T> alloc;
+
+ globalMemCounter.reset();
+ Container<T, test_less<T>, ThrowingAllocator<T>> c(alloc);
+ c.insert_range(in);
+ assert(false); // The function call above should throw.
+
+ } catch (int) {
+ assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+ }
+#endif
+}
+
+template <template <class ...> class Container, class T>
+void test_unord_set_insert_range_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ T in[] = {1, 2};
+
+ try {
+ ThrowingAllocator<T> alloc;
+
+ globalMemCounter.reset();
+ Container<T, test_hash<T>, test_equal_to<T>, ThrowingAllocator<T>> c(alloc);
+ c.insert_range(in);
+ assert(false); // The function call above should throw.
+
+ } catch (int) {
+ assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+ }
+#endif
+}
+
+template <template <class ...> class Container, class K, class V>
+void test_assoc_map_insert_range_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using ValueType = std::pair<const K, V>;
+ ValueType in[] = {
+ ValueType{K{1}, V{1}}
+ };
+
+ try {
+ ThrowingAllocator<ValueType> alloc;
+
+ globalMemCounter.reset();
+ Container<K, V, test_less<K>, ThrowingAllocator<ValueType>> c(alloc);
+ c.insert_range(in);
+ assert(false); // The function call above should throw.
+
+ } catch (int) {
+ assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+ }
+#endif
+}
+
+template <template <class ...> class Container, class K, class V>
+void test_unord_map_insert_range_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using ValueType = std::pair<const K, V>;
+ ValueType in[] = {
+ ValueType{K{1}, V{1}}
+ };
+
+ try {
+ ThrowingAllocator<ValueType> alloc;
+
+ globalMemCounter.reset();
+ Container<K, V, test_hash<K>, test_equal_to<K>, ThrowingAllocator<ValueType>> c(alloc);
+ c.insert_range(in);
+ assert(false); // The function call above should throw.
+
+ } catch (int) {
+ assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+ }
+#endif
+}
+
+#endif // SUPPORT_INSERT_RANGE_MAPS_SETS_H
diff --git a/libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp
index 2fe35df..a6f1a15 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp
@@ -23,20 +23,28 @@
// forward_list(InputIterator first, InputIterator last, const allocator_type& a);
// forward_list(const forward_list& x);
// forward_list(const forward_list& x, const allocator_type& a);
+// template<container-compatible-range<T> R>
+// forward_list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
//
// forward_list& operator=(const forward_list& x);
//
// template <class InputIterator>
// void assign(InputIterator first, InputIterator last);
// void assign(size_type n, const value_type& v);
+// template<container-compatible-range<T> R>
+// void assign_range(R&& rg); // C++23
//
// void push_front(const value_type& v);
+// template<container-compatible-range<T> R>
+// void prepend_range(R&& rg); // C++23
//
// iterator insert_after(const_iterator p, const value_type& v);
// iterator insert_after(const_iterator p, size_type n, const value_type& v);
// template <class InputIterator>
// iterator insert_after(const_iterator p,
// InputIterator first, InputIterator last);
+// template<container-compatible-range<T> R>
+// iterator insert_range_after(const_iterator position, R&& rg); // C++23
//
// void resize(size_type n, const value_type& v);
@@ -44,6 +52,11 @@
#include <cassert>
#include "../../exception_safety_helpers.h"
+#include "test_macros.h"
+
+#if TEST_STD_VER >= 23
+#include <ranges>
+#endif
int main(int, char**) {
{
@@ -90,6 +103,22 @@ int main(int, char**) {
(void)c;
});
+#if TEST_STD_VER >= 23
+ // template<container-compatible-range<T> R>
+ // forward_list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to){
+ {
+ std::forward_list<T> c(std::from_range, std::ranges::subrange(from, to));
+ (void)c;
+ }
+
+ {
+ std::forward_list<T> c(std::from_range, std::ranges::subrange(from, to), Alloc());
+ (void)c;
+ }
+ });
+#endif
+
// template <class InputIterator>
// forward_list(InputIterator first, InputIterator last, const allocator_type& a);
test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to){
@@ -122,12 +151,30 @@ int main(int, char**) {
c.assign(from, to);
});
+#if TEST_STD_VER >= 23
+ // template<container-compatible-range<T> R>
+ // void assign_range(R&& rg); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::forward_list<T> c;
+ c.assign_range(std::ranges::subrange(from, to));
+ });
+#endif
+
// void assign(size_type n, const value_type& v);
test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
std::forward_list<T> c;
c.assign(Size, *from);
});
+#if TEST_STD_VER >= 23
+ // template<container-compatible-range<T> R>
+ // void prepend_range(R&& rg); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::forward_list<T> c;
+ c.prepend_range(std::ranges::subrange(from, to));
+ });
+#endif
+
// iterator insert_after(const_iterator p, size_type n, const value_type& v);
test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
std::forward_list<T> c;
@@ -142,6 +189,15 @@ int main(int, char**) {
c.insert_after(c.before_begin(), from, to);
});
+#if TEST_STD_VER >= 23
+ // template<container-compatible-range<T> R>
+ // iterator insert_range_after(const_iterator position, R&& rg); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::forward_list<T> c;
+ c.insert_range_after(c.before_begin(), std::ranges::subrange(from, to));
+ });
+#endif
+
// void resize(size_type n, const value_type& v);
test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
std::forward_list<T> c;
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/deduct.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/deduct.pass.cpp
index 74eb69b..81b70a9 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/deduct.pass.cpp
@@ -13,8 +13,12 @@
// forward_list(InputIterator, InputIterator, Allocator = Allocator())
// -> forward_list<typename iterator_traits<InputIterator>::value_type, Allocator>;
//
+// template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>>
+// forward_list(from_range_t, R&&, Allocator = Allocator())
+// -> forward_list<ranges::range_value_t<R>, Allocator>; // C++23
#include <algorithm>
+#include <array>
#include <forward_list>
#include <iterator>
#include <cassert>
@@ -128,6 +132,21 @@ int main(int, char**)
}
}
+#if TEST_STD_VER >= 23
+ {
+ {
+ std::forward_list c(std::from_range, std::array<int, 0>());
+ static_assert(std::is_same_v<decltype(c), std::forward_list<int>>);
+ }
+
+ {
+ using Alloc = test_allocator<int>;
+ std::forward_list c(std::from_range, std::array<int, 0>(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::forward_list<int, Alloc>>);
+ }
+ }
+#endif
+
SequenceContainerDeductionGuidesSfinaeAway<std::forward_list, std::forward_list<int>>();
return 0;
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/from_range.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/from_range.pass.cpp
new file mode 100644
index 0000000..312f6db
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/from_range.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<T> R>
+// forward_list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
+
+#include <forward_list>
+
+#include "../../from_range_sequence_containers.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+ test_sequence_container<std::forward_list, int, Iter, Sent, Alloc>([](const auto&) {
+ // No additional validation to do.
+ });
+ });
+ test_sequence_container_move_only<std::forward_list>();
+
+ static_assert(test_constraints<std::forward_list, int, double>());
+
+ test_exception_safety_throwing_copy<std::forward_list>();
+ test_exception_safety_throwing_allocator<std::forward_list, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp
new file mode 100644
index 0000000..a27cc75
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<T> R>
+// constexpr void prepend_range(R&& rg); // C++23
+
+#include <forward_list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+// Tested cases:
+// - different kinds of insertions (prepending an {empty/one-element/mid-sized/long range} into an
+// {empty/one-element/full} container);
+// - prepending move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+int main(int, char**) {
+ static_assert(test_constraints_assign_range<std::forward_list, int, double>());
+
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_sequence_prepend_range<std::forward_list<int, Alloc>, Iter, Sent>([](auto&&) {
+ // No additional validation to do.
+ });
+ });
+ test_sequence_prepend_range_move_only<std::forward_list>();
+
+ test_prepend_range_exception_safety_throwing_copy<std::forward_list>();
+ test_prepend_range_exception_safety_throwing_allocator<std::forward_list, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_range_after.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_range_after.pass.cpp
new file mode 100644
index 0000000..1b9133e
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_range_after.pass.cpp
@@ -0,0 +1,383 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<T> R>
+// constexpr iterator insert_range_after(const_iterator position, R&& rg); // C++23
+
+#include <forward_list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+template <class Container, class Range>
+concept HasInsertRangeAfter = requires (Container& c, Range&& range) {
+ c.insert_range_after(c.begin(), range);
+};
+
+template <template <class...> class Container, class T, class U>
+constexpr bool test_constraints_insert_range_after() {
+ // Input range with the same value type.
+ static_assert(HasInsertRangeAfter<Container<T>, InputRange<T>>);
+ // Input range with a convertible value type.
+ static_assert(HasInsertRangeAfter<Container<T>, InputRange<U>>);
+ // Input range with a non-convertible value type.
+ static_assert(!HasInsertRangeAfter<Container<T>, InputRange<Empty>>);
+ // Not an input range.
+ static_assert(!HasInsertRangeAfter<Container<T>, InputRangeNotDerivedFrom>);
+ static_assert(!HasInsertRangeAfter<Container<T>, InputRangeNotIndirectlyReadable>);
+ static_assert(!HasInsertRangeAfter<Container<T>, InputRangeNotInputOrOutputIterator>);
+
+ return true;
+}
+
+// Tested cases:
+// - different kinds of insertions (inserting an {empty/one-element/mid-sized/long range} into an
+// {empty/one-element/full} container at the {beginning/middle/end});
+// - inserting move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+
+template <class T, class Iter, class Sent, class Alloc>
+constexpr void test_sequence_insert_range_after() {
+ using Container = std::forward_list<T, Alloc>;
+ // Index `0` translates to `before_begin()` and the last index translates to the index before `end()`.
+ auto get_insert_pos = [](auto& c, auto& test_case) { return std::ranges::next(c.before_begin(), test_case.index); };
+ // Unlike `insert_range` in other containers, `insert_range_after` returns the iterator to the last inserted element.
+ auto get_return_pos = [](auto& c, auto& test_case) {
+ return std::ranges::next(c.before_begin(), test_case.index + test_case.input.size());
+ };
+
+ { // Empty container.
+ { // empty_c.insert_range_after(end, empty_range)
+ auto& test_case = EmptyContainer_EmptyRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // empty_c.insert_range_after(end, one_element_range)
+ auto& test_case = EmptyContainer_OneElementRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // empty_c.insert_range_after(end, mid_range)
+ auto& test_case = EmptyContainer_MidRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+ }
+
+ { // One-element container.
+ { // one_element_c.insert_range_after(begin, empty_range)
+ auto& test_case = OneElementContainer_Begin_EmptyRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // one_element_c.insert_range_after(end, empty_range)
+ auto& test_case = OneElementContainer_End_EmptyRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // one_element_c.insert_range_after(begin, one_element_range)
+ auto& test_case = OneElementContainer_Begin_OneElementRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // one_element_c.insert_range_after(end, one_element_range)
+ auto& test_case = OneElementContainer_End_OneElementRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // one_element_c.insert_range_after(begin, mid_range)
+ auto& test_case = OneElementContainer_Begin_MidRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // one_element_c.insert_range_after(end, mid_range)
+ auto& test_case = OneElementContainer_End_MidRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+ }
+
+ { // Full container.
+ { // full_container.insert_range_after(begin, empty_range)
+ auto& test_case = FullContainer_Begin_EmptyRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // full_container.insert_range_after(mid, empty_range)
+ auto& test_case = FullContainer_Mid_EmptyRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // full_container.insert_range_after(end, empty_range)
+ auto& test_case = FullContainer_End_EmptyRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // full_container.insert_range_after(begin, one_element_range)
+ auto& test_case = FullContainer_Begin_OneElementRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // full_container.insert_range_after(end, one_element_range)
+ auto& test_case = FullContainer_Mid_OneElementRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // full_container.insert_range_after(end, one_element_range)
+ auto& test_case = FullContainer_End_OneElementRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // full_container.insert_range_after(begin, mid_range)
+ auto& test_case = FullContainer_Begin_MidRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // full_container.insert_range_after(mid, mid_range)
+ auto& test_case = FullContainer_Mid_MidRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // full_container.insert_range_after(end, mid_range)
+ auto& test_case = FullContainer_End_MidRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // full_container.insert_range_after(begin, long_range)
+ auto& test_case = FullContainer_Begin_LongRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // full_container.insert_range_after(mid, long_range)
+ auto& test_case = FullContainer_Mid_LongRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+
+ { // full_container.insert_range_after(end, long_range)
+ auto& test_case = FullContainer_End_LongRange<T>;
+
+ Container c(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+ auto pos = get_insert_pos(c, test_case);
+
+ auto result = c.insert_range_after(pos, in);
+ assert(std::ranges::equal(c, test_case.expected));
+ assert(result == get_return_pos(c, test_case));
+ }
+ }
+
+ // Also check inserting after `begin()` (the tests above only use `before_begin()`).
+ {
+ Container c{5, 1, 3, 4, 9};
+ Buffer<T> input{-18, -15, -11};
+ auto in = wrap_input<Iter, Sent>(input);
+
+ auto result = c.insert_range_after(c.begin(), in);
+ assert(std::ranges::equal(c, Buffer<int>{5, -18, -15, -11, 1, 3, 4, 9}));
+ assert(result == std::ranges::next(c.begin(), 3));
+ }
+}
+
+void test_sequence_insert_range_after_move_only() {
+ MoveOnly input[5];
+ std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+ std::forward_list<MoveOnly> c;
+ c.insert_range_after(c.before_begin(), in);
+}
+
+void test_insert_range_after_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using T = ThrowingCopy<3>;
+ T::reset();
+ T in[5];
+
+ try {
+ std::forward_list<T> c;
+ c.insert_range_after(c.before_begin(), in);
+ assert(false); // The function call above should throw.
+
+ } catch (int) {
+ assert(T::created_by_copying == 3);
+ assert(T::destroyed == 2); // No destructor call for the partially-constructed element.
+ }
+#endif
+}
+
+template <class T>
+void test_insert_range_after_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ T in[] = {0, 1};
+
+ try {
+ ThrowingAllocator<T> alloc;
+
+ globalMemCounter.reset();
+ std::forward_list<T, ThrowingAllocator<T>> c(alloc);
+ c.insert_range_after(c.before_begin(), in);
+ assert(false); // The function call above should throw.
+
+ } catch (int) {
+ assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+ }
+#endif
+}
+
+int main(int, char**) {
+ static_assert(test_constraints_insert_range_after<std::forward_list, int, double>());
+
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_sequence_insert_range_after<int, Iter, Sent, Alloc>();
+ });
+ test_sequence_insert_range_after_move_only();
+
+ test_insert_range_after_exception_safety_throwing_copy();
+ test_insert_range_after_exception_safety_throwing_allocator<int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp
new file mode 100644
index 0000000..418aa72
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<T> R>
+// constexpr void prepend_range(R&& rg); // C++23
+
+#include <forward_list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+// Tested cases:
+// - different kinds of insertions (prepending an {empty/one-element/mid-sized/long range} into an
+// {empty/one-element/full} container);
+// - prepending move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+int main(int, char**) {
+ static_assert(test_constraints_prepend_range<std::forward_list, int, double>());
+
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_sequence_prepend_range<std::forward_list<int, Alloc>, Iter, Sent>([](auto&&) {
+ // No additional validation to do.
+ });
+ });
+ test_sequence_prepend_range_move_only<std::forward_list>();
+
+ test_prepend_range_exception_safety_throwing_copy<std::forward_list>();
+ test_prepend_range_exception_safety_throwing_allocator<std::forward_list, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/list/exception_safety.pass.cpp b/libcxx/test/std/containers/sequences/list/exception_safety.pass.cpp
new file mode 100644
index 0000000..c734d18
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/exception_safety.pass.cpp
@@ -0,0 +1,225 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <list>
+
+// UNSUPPORTED: c++03, no-exceptions
+
+// TODO:
+// - throwing upon moving;
+// - initializer lists;
+// - throwing when constructing the element in place.
+
+// list(size_type n, const value_type& v);
+// list(size_type n, const value_type& v, const allocator_type& a);
+// template <class InputIterator>
+// list(InputIterator first, InputIterator last);
+// template <class InputIterator>
+// list(InputIterator first, InputIterator last, const allocator_type& a);
+// template<container-compatible-range<T> R>
+// list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
+// list(const list& x);
+// list(const list& x, const allocator_type& a);
+//
+// list& operator=(const list& x);
+//
+// template <class InputIterator>
+// void assign(InputIterator first, InputIterator last);
+// void assign(size_type n, const value_type& v);
+// template<container-compatible-range<T> R>
+// void assign_range(R&& rg); // C++23
+//
+// template<container-compatible-range<T> R>
+// void prepend_range(R&& rg); // C++23
+// void push_back(const value_type& x);
+// template<container-compatible-range<T> R>
+// void append_range(R&& rg); // C++23
+// void push_front(const value_type& v);
+//
+// iterator insert(const_iterator p, const value_type& v);
+// iterator insert(const_iterator p, size_type n, const value_type& v);
+// template <class InputIterator>
+// iterator insert(const_iterator p,
+// InputIterator first, InputIterator last);
+// template<container-compatible-range<T> R>
+// iterator insert_range(const_iterator position, R&& rg); // C++23
+//
+// void resize(size_type n, const value_type& v);
+
+#include <list>
+
+#include <cassert>
+#include "../../exception_safety_helpers.h"
+#include "test_macros.h"
+
+#if TEST_STD_VER >= 23
+#include <ranges>
+#endif
+
+int main(int, char**) {
+ {
+ constexpr int ThrowOn = 1;
+ constexpr int Size = 1;
+ using T = ThrowingCopy<ThrowOn>;
+
+ // void push_front(const value_type& v);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*){
+ std::list<T> c;
+ c.push_front(*from);
+ });
+
+ // void push_back(const value_type& v);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*){
+ std::list<T> c;
+ c.push_back(*from);
+ });
+
+ // iterator insert(const_iterator p, const value_type& v);
+ test_exception_safety_throwing_copy</*ThrowOn=*/1, Size>([](T* from, T*){
+ std::list<T> c;
+ c.insert(c.end(), *from);
+ });
+ }
+
+ {
+ constexpr int ThrowOn = 3;
+ constexpr int Size = 5;
+ using T = ThrowingCopy<ThrowOn>;
+ using C = std::list<T>;
+ using Alloc = std::allocator<T>;
+
+ // list(size_type n, const value_type& v);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*){
+ std::list<T> c(Size, *from);
+ (void)c;
+ });
+
+ // list(size_type n, const value_type& v, const allocator_type& a);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*){
+ std::list<T> c(Size, *from, Alloc());
+ (void)c;
+ });
+
+ // template <class InputIterator>
+ // list(InputIterator first, InputIterator last);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to){
+ std::list<T> c(from, to);
+ (void)c;
+ });
+
+ // template <class InputIterator>
+ // list(InputIterator first, InputIterator last, const allocator_type& a);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to){
+ std::list<T> c(from, to, Alloc());
+ (void)c;
+ });
+
+#if TEST_STD_VER >= 23
+ // template<container-compatible-range<T> R>
+ // list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to){
+ {
+ std::list<T> c(std::from_range, std::ranges::subrange(from, to));
+ (void)c;
+ }
+
+ {
+ std::list<T> c(std::from_range, std::ranges::subrange(from, to), Alloc());
+ (void)c;
+ }
+ });
+#endif
+
+ // list(const list& x);
+ test_exception_safety_throwing_copy_container<C, ThrowOn, Size>([](C&& in) {
+ std::list<T> c(in);
+ (void)c;
+ });
+
+ // list(const list& x, const allocator_type& a);
+ test_exception_safety_throwing_copy_container<C, ThrowOn, Size>([](C&& in) {
+ std::list<T> c(in, Alloc());
+ (void)c;
+ });
+
+ // list& operator=(const list& x);
+ test_exception_safety_throwing_copy_container<C, ThrowOn, Size>([](C&& in) {
+ std::list<T> c;
+ c = in;
+ });
+
+ // template <class InputIterator>
+ // void assign(InputIterator first, InputIterator last);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::list<T> c;
+ c.assign(from, to);
+ });
+
+#if TEST_STD_VER >= 23
+ // template<container-compatible-range<T> R>
+ // void assign_range(R&& rg); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::list<T> c;
+ c.assign_range(std::ranges::subrange(from, to));
+ });
+#endif
+
+ // void assign(size_type n, const value_type& v);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+ std::list<T> c;
+ c.assign(Size, *from);
+ });
+
+#if TEST_STD_VER >= 23
+ // template<container-compatible-range<T> R>
+ // void prepend_range(R&& rg); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::list<T> c;
+ c.prepend_range(std::ranges::subrange(from, to));
+ });
+
+ // template<container-compatible-range<T> R>
+ // void append_range(R&& rg); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::list<T> c;
+ c.append_range(std::ranges::subrange(from, to));
+ });
+#endif
+
+ // iterator insert(const_iterator p, size_type n, const value_type& v);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+ std::list<T> c;
+ c.insert(c.end(), Size, *from);
+ });
+
+ // template <class InputIterator>
+ // iterator insert(const_iterator p,
+ // InputIterator first, InputIterator last);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::list<T> c;
+ c.insert(c.end(), from, to);
+ });
+
+#if TEST_STD_VER >= 23
+ // template<container-compatible-range<T> R>
+ // iterator insert_range(const_iterator position, R&& rg); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::list<T> c;
+ c.insert_range(c.end(), std::ranges::subrange(from, to));
+ });
+#endif
+
+ // void resize(size_type n, const value_type& v);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+ std::list<T> c;
+ c.resize(Size, *from);
+ });
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/list/list.cons/deduct.pass.cpp b/libcxx/test/std/containers/sequences/list/list.cons/deduct.pass.cpp
index a82dac7..2210a9e 100644
--- a/libcxx/test/std/containers/sequences/list/list.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/sequences/list/list.cons/deduct.pass.cpp
@@ -13,12 +13,16 @@
// list(InputIterator, InputIterator, Allocator = Allocator())
// -> list<typename iterator_traits<InputIterator>::value_type, Allocator>;
//
+// template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>>
+// list(from_range_t, R&&, Allocator = Allocator())
+// -> list<ranges::range_value_t<R>, Allocator>; // C++23
-#include <list>
-#include <iterator>
+#include <array>
#include <cassert>
-#include <cstddef>
#include <climits> // INT_MAX
+#include <cstddef>
+#include <iterator>
+#include <list>
#include "deduction_guides_sfinae_checks.h"
#include "test_macros.h"
@@ -127,6 +131,21 @@ int main(int, char**)
}
}
+#if TEST_STD_VER >= 23
+ {
+ {
+ std::list c(std::from_range, std::array<int, 0>());
+ static_assert(std::is_same_v<decltype(c), std::list<int>>);
+ }
+
+ {
+ using Alloc = test_allocator<int>;
+ std::list c(std::from_range, std::array<int, 0>(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::list<int, Alloc>>);
+ }
+ }
+#endif
+
SequenceContainerDeductionGuidesSfinaeAway<std::list, std::list<int>>();
return 0;
diff --git a/libcxx/test/std/containers/sequences/list/list.cons/from_range.pass.cpp b/libcxx/test/std/containers/sequences/list/list.cons/from_range.pass.cpp
new file mode 100644
index 0000000..42b7475
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/list.cons/from_range.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<T> R>
+// list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
+
+#include <list>
+
+#include "../../from_range_sequence_containers.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+ test_sequence_container<std::list, int, Iter, Sent, Alloc>([](const auto&) {
+ // No additional validation to do.
+ });
+ });
+ test_sequence_container_move_only<std::list>();
+
+ static_assert(test_constraints<std::list, int, double>());
+
+ test_exception_safety_throwing_copy<std::list>();
+ test_exception_safety_throwing_allocator<std::list, int>();
+
+ return 0;
+}
+
diff --git a/libcxx/test/std/containers/sequences/list/list.modifiers/append_range.pass.cpp b/libcxx/test/std/containers/sequences/list/list.modifiers/append_range.pass.cpp
new file mode 100644
index 0000000..46a99cb
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/list.modifiers/append_range.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<T> R>
+// constexpr void append_range(R&& rg); // C++23
+
+#include <list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+// Tested cases:
+// - different kinds of insertions (appending an {empty/one-element/mid-sized/long range} into an
+// {empty/one-element/full} container);
+// - appending move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+int main(int, char**) {
+ static_assert(test_constraints_append_range<std::list, int, double>());
+
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_sequence_append_range<std::list<int, Alloc>, Iter, Sent>([](auto&&) {
+ // No additional validation to do.
+ });
+ });
+ test_sequence_append_range_move_only<std::list>();
+
+ test_append_range_exception_safety_throwing_copy<std::list>();
+ test_append_range_exception_safety_throwing_allocator<std::list, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/list/list.modifiers/assign_range.pass.cpp b/libcxx/test/std/containers/sequences/list/list.modifiers/assign_range.pass.cpp
new file mode 100644
index 0000000..d745786
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/list.modifiers/assign_range.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<T> R>
+// constexpr void assign_range(R&& rg); // C++23
+
+#include <list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+// Tested cases:
+// - different kinds of assignments (assigning an {empty/one-element/mid-sized/long range} to an
+// {empty/one-element/full} container);
+// - assigning move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+int main(int, char**) {
+ static_assert(test_constraints_assign_range<std::list, int, double>());
+
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_sequence_assign_range<std::list<int, Alloc>, Iter, Sent>([](auto&&) {
+ // No additional validation to do.
+ });
+ });
+ test_sequence_assign_range_move_only<std::list>();
+
+ test_assign_range_exception_safety_throwing_copy<std::list>();
+ test_assign_range_exception_safety_throwing_allocator<std::list, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/list/list.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/sequences/list/list.modifiers/insert_range.pass.cpp
new file mode 100644
index 0000000..eb3937e
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/list.modifiers/insert_range.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<T> R>
+// constexpr iterator insert_range(const_iterator position, R&& rg); // C++23
+
+#include <list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+// Tested cases:
+// - different kinds of insertions (inserting an {empty/one-element/mid-sized/long range} into an
+// {empty/one-element/full} container at the {beginning/middle/end});
+// - inserting move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+int main(int, char**) {
+ static_assert(test_constraints_insert_range<std::list, int, double>());
+
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_sequence_insert_range<std::list<int, Alloc>, Iter, Sent>([](auto&&) {
+ // No additional validation to do.
+ });
+ });
+ test_sequence_insert_range_move_only<std::list>();
+
+ test_insert_range_exception_safety_throwing_copy<std::list>();
+ test_insert_range_exception_safety_throwing_allocator<std::list, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/list/list.modifiers/prepend_range.pass.cpp b/libcxx/test/std/containers/sequences/list/list.modifiers/prepend_range.pass.cpp
new file mode 100644
index 0000000..d5e4d4f
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/list.modifiers/prepend_range.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<T> R>
+// constexpr void prepend_range(R&& rg); // C++23
+
+#include <list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+// Tested cases:
+// - different kinds of insertions (prepending an {empty/one-element/mid-sized/long range} into an
+// {empty/one-element/full} container);
+// - prepending move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+int main(int, char**) {
+ static_assert(test_constraints_prepend_range<std::list, int, double>());
+
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_sequence_prepend_range<std::list<int, Alloc>, Iter, Sent>([](auto&&) {
+ // No additional validation to do.
+ });
+ });
+ test_sequence_prepend_range_move_only<std::list>();
+
+ test_prepend_range_exception_safety_throwing_copy<std::list>();
+ test_prepend_range_exception_safety_throwing_allocator<std::list, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/from_range_unordered_containers.h b/libcxx/test/std/containers/unord/from_range_unordered_containers.h
new file mode 100644
index 0000000..763dd3e
--- /dev/null
+++ b/libcxx/test/std/containers/unord/from_range_unordered_containers.h
@@ -0,0 +1,435 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUPPORT_FROM_RANGE_UNORDERED_CONTAINERS_H
+#define SUPPORT_FROM_RANGE_UNORDERED_CONTAINERS_H
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <limits>
+#include <ranges>
+#include <vector>
+#include <utility>
+
+#include "../exception_safety_helpers.h"
+#include "../from_range_helpers.h"
+#include "../test_compare.h"
+#include "../test_hash.h"
+#include "MoveOnly.h"
+#include "almost_satisfies_types.h"
+#include "count_new.h"
+#include "test_macros.h"
+
+// template<container-compatible-range<value_type> R>
+// unordered-container(from_range_t, R&& rg, size_type n = see below,
+// const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+// const allocator_type& a = allocator_type()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+// unordered-container(from_range_t, R&& rg, size_type n, const allocator_type& a)
+// : unordered-container(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+//
+// template<container-compatible-range<value_type> R>
+// unordered-container(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+// : unordered-container(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { } // C++23
+
+template <class Container, class Range>
+concept HasFromRangeCtr = requires (Range&& range) {
+ // (from_range, range)
+ Container(std::from_range, std::forward<Range>(range));
+ // (from_range, range, n)
+ Container(std::from_range, std::forward<Range>(range), 0);
+ // (from_range, range, n, hash)
+ Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::key_type>());
+ // (from_range, range, n, hash, equal)
+ Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::key_type>(),
+ std::equal_to<typename Container::key_type>());
+ // (from_range, range, n, hash, equal, alloc)
+ Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::key_type>(),
+ std::equal_to<typename Container::key_type>(), std::allocator<typename Container::value_type>());
+ // (from_range, range, n, alloc)
+ Container(std::from_range, std::forward<Range>(range), 0, std::allocator<typename Container::value_type>());
+ // (from_range, range, n, hash, alloc)
+ Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::key_type>(),
+ std::allocator<typename Container::value_type>());
+};
+
+template <template <class...> class Container, class K, class V, class K2, class V2>
+constexpr bool test_map_constraints() {
+ using ValueType = std::pair<const K, V>;
+
+ // Input range with the same value type.
+ static_assert(HasFromRangeCtr<Container<K, V>, InputRange<ValueType>>);
+ // Input range with a convertible value type.
+ static_assert(HasFromRangeCtr<Container<K, V>, InputRange<std::pair<const K2, V2>>>);
+ // Input range with a non-convertible value type.
+ static_assert(!HasFromRangeCtr<Container<K, V>, InputRange<std::pair<const Empty, V>>>);
+ static_assert(!HasFromRangeCtr<Container<K, V>, InputRange<std::pair<const K, Empty>>>);
+ // Not an input range.
+ static_assert(!HasFromRangeCtr<Container<K, V>, InputRangeNotDerivedFromGeneric<ValueType>>);
+
+ return true;
+}
+
+template <template <class ...> class Container,
+ class K,
+ class V,
+ class Iter,
+ class Sent,
+ class Hash,
+ class Equal,
+ class Alloc,
+ class ValueType = std::pair<const K, V>>
+void test_unordered_map_with_input(std::vector<ValueType>&& input) {
+ using DefaultHash = std::hash<int>;
+ using DefaultEqual = std::equal_to<int>;
+
+ auto validate = [](auto&& c) {
+ if (!c.empty()) {
+ auto diff = c.load_factor() - (static_cast<float>(c.size()) / c.bucket_count());
+ assert(std::fabs(diff) < std::numeric_limits<float>::epsilon());
+ }
+ assert(c.max_load_factor() == 1);
+ };
+
+ auto in = wrap_input<Iter, Sent>(input);
+
+ { // (range)
+ Container<K, V> c(std::from_range, in);
+
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+
+ { // (range, n)
+ Container<K, V> c(std::from_range, in, 123);
+
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+
+ { // (range, n, hasher)
+ Container<K, V, Hash> c(std::from_range, in, 123, Hash());
+
+ assert(c.hash_function() == Hash());
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+
+ { // (range, n, hasher, key_equal)
+ Container<K, V, Hash, Equal> c(std::from_range, in, 123, Hash(), Equal());
+
+ assert(c.hash_function() == Hash());
+ assert(c.key_eq() == Equal());
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+
+ { // (range, n, hasher, key_equal, allocator)
+ Alloc alloc;
+ Container<K, V, Hash, Equal, Alloc> c(std::from_range, in, 123, Hash(), Equal(), alloc);
+
+ assert(c.hash_function() == Hash());
+ assert(c.key_eq() == Equal());
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+
+ { // (range, n, allocator)
+ Alloc alloc;
+ Container<K, V, DefaultHash, DefaultEqual, Alloc> c(std::from_range, in, 123, alloc);
+
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+
+ { // (range, n, hasher, allocator)
+ Alloc alloc;
+ Container<K, V, Hash, DefaultEqual, Alloc> c(std::from_range, in, 123, Hash(), alloc);
+
+ assert(c.hash_function() == Hash());
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+}
+
+template <template <class ...> class Container,
+ class K,
+ class V,
+ class Iter,
+ class Sent,
+ class Hash,
+ class Equal,
+ class Alloc>
+void test_unordered_map() {
+ auto test_with_input = &test_unordered_map_with_input<Container, K, V, Iter, Sent, Hash, Equal, Alloc>;
+
+ // Normal input.
+ test_with_input({{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}});
+ // Empty input.
+ test_with_input({});
+ // Single-element input.
+ test_with_input({{1, 2}});
+}
+
+template <template <class ...> class Container>
+void test_unordered_map_move_only() {
+ std::pair<const int, MoveOnly> input[5];
+ std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+ [[maybe_unused]] Container<int, MoveOnly> c(std::from_range, in);
+}
+
+template <template <class ...> class Container>
+void test_map_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using K = int;
+ using V = ThrowingCopy<3>;
+
+ V::throwing_enabled = false;
+ std::pair<const K, V> in[5] = {
+ {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}
+ };
+ V::throwing_enabled = true;
+ V::reset();
+
+ try {
+ Container<K, V> c(std::from_range, in);
+ assert(false); // The constructor call above should throw.
+
+ } catch (int) {
+ assert(V::created_by_copying == 3);
+ assert(V::destroyed == 2); // No destructor call for the partially-constructed element.
+ }
+#endif
+}
+
+template <template <class ...> class Container, class K, class V>
+void test_map_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using ValueType = std::pair<const K, V>;
+ ValueType in[] = {
+ ValueType{K{1}, V{1}}
+ };
+
+ try {
+ ThrowingAllocator<ValueType> alloc;
+
+ globalMemCounter.reset();
+ Container<K, V, test_hash<K>, test_equal_to<K>, ThrowingAllocator<ValueType>>
+ c(std::from_range, in, /*n=*/0, alloc);
+ assert(false); // The constructor call should throw.
+
+ } catch (int) {
+ assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+ }
+#endif
+}
+
+template <class Container, class Range>
+concept SetHasFromRangeCtr = requires (Range&& range) {
+ // (from_range, range)
+ Container(std::from_range, std::forward<Range>(range));
+ // (from_range, range, n)
+ Container(std::from_range, std::forward<Range>(range), 0);
+ // (from_range, range, n, hash)
+ Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::value_type>());
+ // (from_range, range, n, hash, equal)
+ Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::value_type>(),
+ std::equal_to<typename Container::value_type>());
+ // (from_range, range, n, hash, equal, alloc)
+ Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::value_type>(),
+ std::equal_to<typename Container::value_type>(), std::allocator<typename Container::value_type>());
+ // (from_range, range, n, alloc)
+ Container(std::from_range, std::forward<Range>(range), 0, std::allocator<typename Container::value_type>());
+ // (from_range, range, n, hash, alloc)
+ Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::value_type>(),
+ std::allocator<typename Container::value_type>());
+};
+
+template <template <class...> class Container, class T, class U>
+constexpr bool test_set_constraints() {
+ // Input range with the same value type.
+ static_assert(HasFromRangeCtr<Container<T>, InputRange<T>>);
+ // Input range with a convertible value type.
+ static_assert(HasFromRangeCtr<Container<T>, InputRange<U>>);
+ // Input range with a non-convertible value type.
+ static_assert(!HasFromRangeCtr<Container<T>, InputRange<Empty>>);
+ // Not an input range.
+ static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotDerivedFromGeneric<T>>);
+
+ return true;
+}
+
+template <template <class ...> class Container,
+ class T,
+ class Iter,
+ class Sent,
+ class Hash,
+ class Equal,
+ class Alloc>
+void test_unordered_set_with_input(std::vector<T>&& input) {
+ using DefaultHash = std::hash<int>;
+ using DefaultEqual = std::equal_to<int>;
+
+ auto validate = [](auto&& c) {
+ if (!c.empty()) {
+ auto diff = c.load_factor() - (static_cast<float>(c.size()) / c.bucket_count());
+ assert(std::fabs(diff) < std::numeric_limits<float>::epsilon());
+ }
+ assert(c.max_load_factor() == 1);
+ };
+
+ auto b = Iter(input.data());
+ auto e = Iter(input.data() + input.size());
+ std::ranges::subrange in(std::move(b), Sent(std::move(e)));
+
+ { // (range)
+ Container<T> c(std::from_range, in);
+
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+
+ { // (range, n)
+ Container<T> c(std::from_range, in, 123);
+
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+
+ { // (range, n, hasher)
+ Container<T, Hash> c(std::from_range, in, 123, Hash());
+
+ assert(c.hash_function() == Hash());
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+
+ { // (range, n, hasher, key_equal)
+ Container<T, Hash, Equal> c(std::from_range, in, 123, Hash(), Equal());
+
+ assert(c.hash_function() == Hash());
+ assert(c.key_eq() == Equal());
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+
+ { // (range, n, hasher, key_equal, allocator)
+ Alloc alloc;
+ Container<T, Hash, Equal, Alloc> c(std::from_range, in, 123, Hash(), Equal(), alloc);
+
+ assert(c.hash_function() == Hash());
+ assert(c.key_eq() == Equal());
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+
+ { // (range, n, allocator)
+ Alloc alloc;
+ Container<T, DefaultHash, DefaultEqual, Alloc> c(std::from_range, in, 123, alloc);
+
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+
+ { // (range, n, hasher, allocator)
+ Alloc alloc;
+ Container<T, Hash, DefaultEqual, Alloc> c(std::from_range, in, 123, Hash(), alloc);
+
+ assert(c.hash_function() == Hash());
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ validate(c);
+ }
+}
+
+template <template <class ...> class Container,
+ class T,
+ class Iter,
+ class Sent,
+ class Hash,
+ class Equal,
+ class Alloc>
+void test_unordered_set() {
+ auto test_with_input = &test_unordered_set_with_input<Container, T, Iter, Sent, Hash, Equal, Alloc>;
+
+ // Normal input.
+ test_with_input({0, 5, 12, 7, -1, 8, 26});
+ // Empty input.
+ test_with_input({});
+ // Single-element input.
+ test_with_input({5});
+}
+
+template <template <class ...> class Container>
+void test_unordered_set_move_only() {
+ MoveOnly input[5];
+ std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+ [[maybe_unused]] Container<MoveOnly> c(std::from_range, in);
+}
+
+template <template <class ...> class Container>
+void test_set_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using T = ThrowingCopy<3>;
+ T::reset();
+ T in[5] = {{1}, {2}, {3}, {4}, {5}};
+
+ try {
+ Container<T, test_hash<T>> c(std::from_range, in);
+ assert(false); // The constructor call above should throw.
+
+ } catch (int) {
+ assert(T::created_by_copying == 3);
+ assert(T::destroyed == 2); // No destructor call for the partially-constructed element.
+ }
+#endif
+}
+
+template <template <class ...> class Container, class T>
+void test_set_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ T in[] = {1, 2, 3};
+
+ try {
+ ThrowingAllocator<T> alloc;
+
+ globalMemCounter.reset();
+ Container<T, test_hash<T>, test_equal_to<T>, ThrowingAllocator<T>> c(std::from_range, in, /*n=*/0, alloc);
+ assert(false); // The constructor call should throw.
+
+ } catch (int) {
+ assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+ }
+#endif
+}
+
+#endif // SUPPORT_FROM_RANGE_UNORDERED_CONTAINERS_H
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/deduct.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/deduct.pass.cpp
index 9a36348..873b77e 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/deduct.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/deduct.pass.cpp
@@ -54,14 +54,39 @@
// unordered_map(initializer_list<pair<Key, T>>, typename see below::size_type, Hash,
// Allocator)
// -> unordered_map<Key, T, Hash, equal_to<Key>, Allocator>;
+//
+// template<ranges::input_range R, class Hash = hash<range-key-type<R>>,
+// class Pred = equal_to<range-key-type<R>>,
+// class Allocator = allocator<range-to-alloc-type<R>>>
+// unordered_map(from_range_t, R&&, typename see below::size_type = see below,
+// Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+// -> unordered_map<range-key-type<R>, range-mapped-type<R>, Hash, Pred, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+// unordered_map(from_range_t, R&&, typename see below::size_type, Allocator)
+// -> unordered_map<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+// equal_to<range-key-type<R>>, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+// unordered_map(from_range_t, R&&, Allocator)
+// -> unordered_map<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+// equal_to<range-key-type<R>>, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Hash, class Allocator>
+// unordered_map(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+// -> unordered_map<range-key-type<R>, range-mapped-type<R>, Hash,
+// equal_to<range-key-type<R>>, Allocator>; // C++23
#include <algorithm> // is_permutation
+#include <array>
#include <cassert>
#include <climits> // INT_MAX
#include <iterator>
#include <type_traits>
#include <unordered_map>
+#include "../../../test_compare.h"
+#include "../../../test_hash.h"
#include "deduction_guides_sfinae_checks.h"
#include "test_allocator.h"
@@ -220,6 +245,58 @@ int main(int, char**)
ASSERT_SAME_TYPE(decltype(m2), std::unordered_map<int, int>);
}
+#if TEST_STD_VER >= 23
+ {
+ using Range = std::array<P, 0>;
+ using Pred = test_equal_to<int>;
+ using DefaultPred = std::equal_to<int>;
+ using Hash = test_hash<int>;
+ using DefaultHash = std::hash<int>;
+ using Alloc = test_allocator<PC>;
+
+ { // (from_range, range)
+ std::unordered_map c(std::from_range, Range());
+ static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long>>);
+ }
+
+ { // (from_range, range, n)
+ std::unordered_map c(std::from_range, Range(), std::size_t());
+ static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long>>);
+ }
+
+ { // (from_range, range, n, hash)
+ std::unordered_map c(std::from_range, Range(), std::size_t(), Hash());
+ static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, Hash>>);
+ }
+
+ { // (from_range, range, n, hash, pred)
+ std::unordered_map c(std::from_range, Range(), std::size_t(), Hash(), Pred());
+ static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, Hash, Pred>>);
+ }
+
+ { // (from_range, range, n, hash, pred, alloc)
+ std::unordered_map c(std::from_range, Range(), std::size_t(), Hash(), Pred(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, Hash, Pred, Alloc>>);
+ }
+
+ { // (from_range, range, n, alloc)
+ std::unordered_map c(std::from_range, Range(), std::size_t(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, DefaultHash, DefaultPred, Alloc>>);
+ }
+
+ // TODO(LWG 2713): uncomment this test once the constructor is added.
+ { // (from_range, range, alloc)
+ //std::unordered_map c(std::from_range, Range(), Alloc());
+ //static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, DefaultHash, DefaultPred, Alloc>>);
+ }
+
+ { // (from_range, range, n, hash, alloc)
+ std::unordered_map c(std::from_range, Range(), std::size_t(), Hash(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, Hash, DefaultPred, Alloc>>);
+ }
+ }
+#endif
+
UnorderedContainerDeductionGuidesSfinaeAway<std::unordered_map, std::unordered_map<int, long>>();
return 0;
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/from_range.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/from_range.pass.cpp
new file mode 100644
index 0000000..f48057c
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/from_range.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<value_type> R>
+// unordered_map(from_range_t, R&& rg, size_type n = see below,
+// const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+// const allocator_type& a = allocator_type()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+// unordered_map(from_range_t, R&& rg, size_type n, const allocator_type& a)
+// : unordered_map(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+//
+// template<container-compatible-range<value_type> R>
+// unordered_map(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+// : unordered_map(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { } // C++23
+
+#include <array>
+#include <unordered_map>
+
+#include "../../from_range_unordered_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+ using T = std::pair<const int, char>;
+
+ std::array input = {
+ T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}
+ };
+ std::array expected = {
+ T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{4, 'a'}
+ };
+ auto c = std::unordered_map<int, char>(std::from_range, input);
+ assert(std::ranges::is_permutation(expected, c));
+}
+
+int main(int, char**) {
+ using T = std::pair<const int, int>;
+ for_all_iterators_and_allocators<T>([]<class Iter, class Sent, class Alloc>() {
+ test_unordered_map<std::unordered_map, int, int, Iter, Sent, test_hash<int>, test_equal_to<int>, Alloc>();
+ });
+ test_unordered_map_move_only<std::unordered_map>();
+ test_duplicates();
+
+ static_assert(test_map_constraints<std::unordered_map, int, int, double, double>());
+
+ test_map_exception_safety_throwing_copy<std::unordered_map>();
+ test_map_exception_safety_throwing_allocator<std::unordered_map, int, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_iter_iter.pass.cpp
new file mode 100644
index 0000000..342ff58
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_iter_iter.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <unordered_map>
+
+// template <class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>,
+// class Alloc = allocator<pair<const Key, T>>>
+// class unordered_map
+
+// template <class InputIterator>
+// void insert(InputIterator first, InputIterator last);
+
+#include <unordered_map>
+#include <string>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "min_allocator.h"
+
+int main(int, char**)
+{
+ {
+ typedef std::unordered_map<int, std::string> C;
+ typedef std::pair<int, std::string> P;
+ P a[] =
+ {
+ P(1, "one"),
+ P(2, "two"),
+ P(3, "three"),
+ P(4, "four"),
+ P(1, "four"),
+ P(2, "four"),
+ };
+ C c;
+ c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+ assert(c.size() == 4);
+ assert(c.at(1) == "one");
+ assert(c.at(2) == "two");
+ assert(c.at(3) == "three");
+ assert(c.at(4) == "four");
+ }
+#if TEST_STD_VER >= 11
+ {
+ typedef std::unordered_map<int, std::string, std::hash<int>, std::equal_to<int>,
+ min_allocator<std::pair<const int, std::string>>> C;
+ typedef std::pair<int, std::string> P;
+ P a[] =
+ {
+ P(1, "one"),
+ P(2, "two"),
+ P(3, "three"),
+ P(4, "four"),
+ P(1, "four"),
+ P(2, "four"),
+ };
+ C c;
+ c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+ assert(c.size() == 4);
+ assert(c.at(1) == "one");
+ assert(c.at(2) == "two");
+ assert(c.at(3) == "three");
+ assert(c.at(4) == "four");
+ }
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_range.pass.cpp
index 342ff58..8b00433 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_range.pass.cpp
@@ -6,68 +6,35 @@
//
//===----------------------------------------------------------------------===//
-// <unordered_map>
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
-// template <class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>,
-// class Alloc = allocator<pair<const Key, T>>>
-// class unordered_map
+// <unordered_map>
-// template <class InputIterator>
-// void insert(InputIterator first, InputIterator last);
+// template<container-compatible-range<value_type> R>
+// void insert_range(R&& rg); // C++23
#include <unordered_map>
-#include <string>
-#include <cassert>
+#include "../../../insert_range_maps_sets.h"
#include "test_macros.h"
-#include "test_iterators.h"
-#include "min_allocator.h"
-int main(int, char**)
-{
- {
- typedef std::unordered_map<int, std::string> C;
- typedef std::pair<int, std::string> P;
- P a[] =
- {
- P(1, "one"),
- P(2, "two"),
- P(3, "three"),
- P(4, "four"),
- P(1, "four"),
- P(2, "four"),
- };
- C c;
- c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
- assert(c.size() == 4);
- assert(c.at(1) == "one");
- assert(c.at(2) == "two");
- assert(c.at(3) == "three");
- assert(c.at(4) == "four");
- }
-#if TEST_STD_VER >= 11
- {
- typedef std::unordered_map<int, std::string, std::hash<int>, std::equal_to<int>,
- min_allocator<std::pair<const int, std::string>>> C;
- typedef std::pair<int, std::string> P;
- P a[] =
- {
- P(1, "one"),
- P(2, "two"),
- P(3, "three"),
- P(4, "four"),
- P(1, "four"),
- P(2, "four"),
- };
- C c;
- c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
- assert(c.size() == 4);
- assert(c.at(1) == "one");
- assert(c.at(2) == "two");
- assert(c.at(3) == "three");
- assert(c.at(4) == "four");
- }
-#endif
+int main(int, char**) {
+ // Note: we want to use a pair with non-const elements for input (an assignable type is a lot more convenient) but
+ // have to use the exact `value_type` of the map (that is, `pair<const K, V>`) for the allocator.
+ using Pair = std::pair<int, char>;
+ using ConstPair = std::pair<const int, char>;
+ for_all_iterators_and_allocators<ConstPair, const Pair*>([]<class Iter, class Sent, class Alloc>() {
+ test_map_set_insert_range<std::unordered_map<int, char, test_hash<int>, test_equal_to<int>, Alloc>, Pair, Iter, Sent>();
+ });
+
+ static_assert(test_map_constraints_insert_range<std::unordered_map, int, int, char, double>());
+
+ test_map_insert_range_move_only<std::unordered_map>();
+
+ test_map_insert_range_exception_safety_throwing_copy<std::unordered_map>();
+ test_unord_map_insert_range_exception_safety_throwing_allocator<std::unordered_map, int, int>();
return 0;
}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/deduct.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/deduct.pass.cpp
index 527cc4c..7e93032 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/deduct.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/deduct.pass.cpp
@@ -54,13 +54,38 @@
// unordered_multimap(initializer_list<pair<Key, T>>, typename see below::size_type, Hash,
// Allocator)
// -> unordered_multimap<Key, T, Hash, equal_to<Key>, Allocator>;
+//
+// template<ranges::input_range R, class Hash = hash<range-key-type<R>>,
+// class Pred = equal_to<range-key-type<R>>,
+// class Allocator = allocator<range-to-alloc-type<R>>>
+// unordered_multimap(from_range_t, R&&, typename see below::size_type = see below,
+// Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+// -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, Hash, Pred, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+// unordered_multimap(from_range_t, R&&, typename see below::size_type, Allocator)
+// -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+// equal_to<range-key-type<R>>, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+// unordered_multimap(from_range_t, R&&, Allocator)
+// -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+// equal_to<range-key-type<R>>, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Hash, class Allocator>
+// unordered_multimap(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+// -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, Hash,
+// equal_to<range-key-type<R>>, Allocator>; // C++23
#include <algorithm> // is_permutation
+#include <array>
#include <cassert>
#include <climits> // INT_MAX
#include <type_traits>
#include <unordered_map>
+#include "../../../test_compare.h"
+#include "../../../test_hash.h"
#include "deduction_guides_sfinae_checks.h"
#include "test_allocator.h"
@@ -219,6 +244,58 @@ int main(int, char**)
ASSERT_SAME_TYPE(decltype(m2), std::unordered_multimap<int, int>);
}
+#if TEST_STD_VER >= 23
+ {
+ using Range = std::array<P, 0>;
+ using Pred = test_equal_to<int>;
+ using DefaultPred = std::equal_to<int>;
+ using Hash = test_hash<int>;
+ using DefaultHash = std::hash<int>;
+ using Alloc = test_allocator<PC>;
+
+ { // (from_range, range)
+ std::unordered_multimap c(std::from_range, Range());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long>>);
+ }
+
+ { // (from_range, range, n)
+ std::unordered_multimap c(std::from_range, Range(), std::size_t());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long>>);
+ }
+
+ { // (from_range, range, n, hash)
+ std::unordered_multimap c(std::from_range, Range(), std::size_t(), Hash());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long, Hash>>);
+ }
+
+ { // (from_range, range, n, hash, pred)
+ std::unordered_multimap c(std::from_range, Range(), std::size_t(), Hash(), Pred());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long, Hash, Pred>>);
+ }
+
+ { // (from_range, range, n, hash, pred, alloc)
+ std::unordered_multimap c(std::from_range, Range(), std::size_t(), Hash(), Pred(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long, Hash, Pred, Alloc>>);
+ }
+
+ { // (from_range, range, n, alloc)
+ std::unordered_multimap c(std::from_range, Range(), std::size_t(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long, DefaultHash, DefaultPred, Alloc>>);
+ }
+
+ // TODO(LWG 2713): uncomment this test once the constructor is added.
+ { // (from_range, range, alloc)
+ //std::unordered_multimap c(std::from_range, Range(), Alloc());
+ //static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long, DefaultHash, DefaultPred, Alloc>>);
+ }
+
+ { // (from_range, range, n, hash, alloc)
+ std::unordered_multimap c(std::from_range, Range(), std::size_t(), Hash(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long, Hash, DefaultPred, Alloc>>);
+ }
+ }
+#endif
+
UnorderedContainerDeductionGuidesSfinaeAway<std::unordered_multimap, std::unordered_multimap<int, long>>();
return 0;
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/from_range.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/from_range.pass.cpp
new file mode 100644
index 0000000..4fe95da
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/from_range.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<value_type> R>
+// unordered_multimap(from_range_t, R&& rg, size_type n = see below,
+// const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+// const allocator_type& a = allocator_type()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+// unordered_multimap(from_range_t, R&& rg, size_type n, const allocator_type& a)
+// : unordered_multimap(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+//
+// template<container-compatible-range<value_type> R>
+// unordered_multimap(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+// : unordered_multimap(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { } // C++23
+
+#include <array>
+#include <unordered_map>
+
+#include "../../from_range_unordered_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+ using T = std::pair<const int, char>;
+ std::array input = {
+ T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}
+ };
+ auto c = std::unordered_multimap<int, char>(std::from_range, input);
+ assert(std::ranges::is_permutation(input, c));
+}
+
+int main(int, char**) {
+ using T = std::pair<const int, int>;
+ for_all_iterators_and_allocators<T>([]<class Iter, class Sent, class Alloc>() {
+ test_unordered_map<std::unordered_multimap, int, int, Iter, Sent, test_hash<int>, test_equal_to<int>, Alloc>();
+ });
+ test_unordered_map_move_only<std::unordered_multimap>();
+ test_duplicates();
+
+ static_assert(test_map_constraints<std::unordered_multimap, int, int, double, double>());
+
+ test_map_exception_safety_throwing_copy<std::unordered_multimap>();
+ test_map_exception_safety_throwing_allocator<std::unordered_multimap, int, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_iter_iter.pass.cpp
new file mode 100644
index 0000000..519ed7a
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_iter_iter.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <unordered_map>
+
+// template <class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>,
+// class Alloc = allocator<pair<const Key, T>>>
+// class unordered_multimap
+
+// template <class InputIterator>
+// void insert(InputIterator first, InputIterator last);
+
+#include <unordered_map>
+#include <string>
+#include <set>
+#include <cassert>
+#include <cstddef>
+
+#include "test_macros.h"
+#include "../../../check_consecutive.h"
+#include "test_iterators.h"
+#include "min_allocator.h"
+
+int main(int, char**)
+{
+ {
+ typedef std::unordered_multimap<int, std::string> C;
+ typedef std::pair<int, std::string> P;
+ P a[] =
+ {
+ P(1, "one"),
+ P(2, "two"),
+ P(3, "three"),
+ P(4, "four"),
+ P(1, "four"),
+ P(2, "four"),
+ };
+ C c;
+ c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+ assert(c.size() == 6);
+ typedef std::pair<C::iterator, C::iterator> Eq;
+ Eq eq = c.equal_range(1);
+ assert(std::distance(eq.first, eq.second) == 2);
+ std::multiset<std::string> s;
+ s.insert("one");
+ s.insert("four");
+ CheckConsecutiveKeys<C::const_iterator>(c.find(1), c.end(), 1, s);
+ eq = c.equal_range(2);
+ assert(std::distance(eq.first, eq.second) == 2);
+ s.insert("two");
+ s.insert("four");
+ CheckConsecutiveKeys<C::const_iterator>(c.find(2), c.end(), 2, s);
+ eq = c.equal_range(3);
+ assert(std::distance(eq.first, eq.second) == 1);
+ C::iterator k = eq.first;
+ assert(k->first == 3);
+ assert(k->second == "three");
+ eq = c.equal_range(4);
+ assert(std::distance(eq.first, eq.second) == 1);
+ k = eq.first;
+ assert(k->first == 4);
+ assert(k->second == "four");
+ assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
+ assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
+ }
+#if TEST_STD_VER >= 11
+ {
+ typedef std::unordered_multimap<int, std::string, std::hash<int>, std::equal_to<int>,
+ min_allocator<std::pair<const int, std::string>>> C;
+ typedef std::pair<int, std::string> P;
+ P a[] =
+ {
+ P(1, "one"),
+ P(2, "two"),
+ P(3, "three"),
+ P(4, "four"),
+ P(1, "four"),
+ P(2, "four"),
+ };
+ C c;
+ c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+ assert(c.size() == 6);
+ typedef std::pair<C::iterator, C::iterator> Eq;
+ Eq eq = c.equal_range(1);
+ assert(std::distance(eq.first, eq.second) == 2);
+ std::multiset<std::string> s;
+ s.insert("one");
+ s.insert("four");
+ CheckConsecutiveKeys<C::const_iterator>(c.find(1), c.end(), 1, s);
+ eq = c.equal_range(2);
+ assert(std::distance(eq.first, eq.second) == 2);
+ s.insert("two");
+ s.insert("four");
+ CheckConsecutiveKeys<C::const_iterator>(c.find(2), c.end(), 2, s);
+ eq = c.equal_range(3);
+ assert(std::distance(eq.first, eq.second) == 1);
+ C::iterator k = eq.first;
+ assert(k->first == 3);
+ assert(k->second == "three");
+ eq = c.equal_range(4);
+ assert(std::distance(eq.first, eq.second) == 1);
+ k = eq.first;
+ assert(k->first == 4);
+ assert(k->second == "four");
+ assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
+ assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
+ }
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_range.pass.cpp
index 519ed7a..fcde119 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_range.pass.cpp
@@ -6,111 +6,36 @@
//
//===----------------------------------------------------------------------===//
-// <unordered_map>
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
-// template <class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>,
-// class Alloc = allocator<pair<const Key, T>>>
-// class unordered_multimap
+// <unordered_map>
-// template <class InputIterator>
-// void insert(InputIterator first, InputIterator last);
+// template<container-compatible-range<value_type> R>
+// void insert_range(R&& rg); // C++23
#include <unordered_map>
-#include <string>
-#include <set>
-#include <cassert>
-#include <cstddef>
+#include "../../../insert_range_maps_sets.h"
#include "test_macros.h"
-#include "../../../check_consecutive.h"
-#include "test_iterators.h"
-#include "min_allocator.h"
-int main(int, char**)
-{
- {
- typedef std::unordered_multimap<int, std::string> C;
- typedef std::pair<int, std::string> P;
- P a[] =
- {
- P(1, "one"),
- P(2, "two"),
- P(3, "three"),
- P(4, "four"),
- P(1, "four"),
- P(2, "four"),
- };
- C c;
- c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
- assert(c.size() == 6);
- typedef std::pair<C::iterator, C::iterator> Eq;
- Eq eq = c.equal_range(1);
- assert(std::distance(eq.first, eq.second) == 2);
- std::multiset<std::string> s;
- s.insert("one");
- s.insert("four");
- CheckConsecutiveKeys<C::const_iterator>(c.find(1), c.end(), 1, s);
- eq = c.equal_range(2);
- assert(std::distance(eq.first, eq.second) == 2);
- s.insert("two");
- s.insert("four");
- CheckConsecutiveKeys<C::const_iterator>(c.find(2), c.end(), 2, s);
- eq = c.equal_range(3);
- assert(std::distance(eq.first, eq.second) == 1);
- C::iterator k = eq.first;
- assert(k->first == 3);
- assert(k->second == "three");
- eq = c.equal_range(4);
- assert(std::distance(eq.first, eq.second) == 1);
- k = eq.first;
- assert(k->first == 4);
- assert(k->second == "four");
- assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
- assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
- }
-#if TEST_STD_VER >= 11
- {
- typedef std::unordered_multimap<int, std::string, std::hash<int>, std::equal_to<int>,
- min_allocator<std::pair<const int, std::string>>> C;
- typedef std::pair<int, std::string> P;
- P a[] =
- {
- P(1, "one"),
- P(2, "two"),
- P(3, "three"),
- P(4, "four"),
- P(1, "four"),
- P(2, "four"),
- };
- C c;
- c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
- assert(c.size() == 6);
- typedef std::pair<C::iterator, C::iterator> Eq;
- Eq eq = c.equal_range(1);
- assert(std::distance(eq.first, eq.second) == 2);
- std::multiset<std::string> s;
- s.insert("one");
- s.insert("four");
- CheckConsecutiveKeys<C::const_iterator>(c.find(1), c.end(), 1, s);
- eq = c.equal_range(2);
- assert(std::distance(eq.first, eq.second) == 2);
- s.insert("two");
- s.insert("four");
- CheckConsecutiveKeys<C::const_iterator>(c.find(2), c.end(), 2, s);
- eq = c.equal_range(3);
- assert(std::distance(eq.first, eq.second) == 1);
- C::iterator k = eq.first;
- assert(k->first == 3);
- assert(k->second == "three");
- eq = c.equal_range(4);
- assert(std::distance(eq.first, eq.second) == 1);
- k = eq.first;
- assert(k->first == 4);
- assert(k->second == "four");
- assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
- assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
- }
-#endif
+int main(int, char**) {
+ // Note: we want to use a pair with non-const elements for input (an assignable type is a lot more convenient) but
+ // have to use the exact `value_type` of the map (that is, `pair<const K, V>`) for the allocator.
+ using Pair = std::pair<int, char>;
+ using ConstPair = std::pair<const int, char>;
+ for_all_iterators_and_allocators<ConstPair, const Pair*>([]<class Iter, class Sent, class Alloc>() {
+ test_map_set_insert_range<std::unordered_multimap<int, char, test_hash<int>, test_equal_to<int>, Alloc>, Pair, Iter, Sent>(/*allow_duplicates=*/true);
+ });
+
+ static_assert(test_map_constraints_insert_range<std::unordered_multimap, int, int, char, double>());
+
+ test_map_insert_range_move_only<std::unordered_multimap>();
+
+ test_map_insert_range_exception_safety_throwing_copy<std::unordered_multimap>();
+ test_unord_map_insert_range_exception_safety_throwing_allocator<std::unordered_multimap, int, int>();
return 0;
}
+
diff --git a/libcxx/test/std/containers/unord/unord.multiset/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/insert_iter_iter.pass.cpp
new file mode 100644
index 0000000..f893a9d
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/insert_iter_iter.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <unordered_set>
+
+// template <class Value, class Hash = hash<Value>, class Pred = equal_to<Value>,
+// class Alloc = allocator<Value>>
+// class unordered_multiset
+
+// template <class InputIterator>
+// void insert(InputIterator first, InputIterator last);
+
+#include <unordered_set>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "min_allocator.h"
+
+int main(int, char**)
+{
+ {
+ typedef std::unordered_multiset<int> C;
+ typedef int P;
+ P a[] =
+ {
+ P(1),
+ P(2),
+ P(3),
+ P(4),
+ P(1),
+ P(2)
+ };
+ C c;
+ c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+ assert(c.size() == 6);
+ assert(c.count(1) == 2);
+ assert(c.count(2) == 2);
+ assert(c.count(3) == 1);
+ assert(c.count(4) == 1);
+ }
+#if TEST_STD_VER >= 11
+ {
+ typedef std::unordered_multiset<int, std::hash<int>,
+ std::equal_to<int>, min_allocator<int>> C;
+ typedef int P;
+ P a[] =
+ {
+ P(1),
+ P(2),
+ P(3),
+ P(4),
+ P(1),
+ P(2)
+ };
+ C c;
+ c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+ assert(c.size() == 6);
+ assert(c.count(1) == 2);
+ assert(c.count(2) == 2);
+ assert(c.count(3) == 1);
+ assert(c.count(4) == 1);
+ }
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multiset/insert_range.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/insert_range.pass.cpp
index f893a9d..73ac4cf 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/insert_range.pass.cpp
@@ -6,67 +6,32 @@
//
//===----------------------------------------------------------------------===//
-// <unordered_set>
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
-// template <class Value, class Hash = hash<Value>, class Pred = equal_to<Value>,
-// class Alloc = allocator<Value>>
-// class unordered_multiset
+// <set>
-// template <class InputIterator>
-// void insert(InputIterator first, InputIterator last);
+// template<container-compatible-range<value_type> R>
+// void insert_range(R&& rg); // C++23
#include <unordered_set>
-#include <cassert>
+#include "../../insert_range_maps_sets.h"
#include "test_macros.h"
-#include "test_iterators.h"
-#include "min_allocator.h"
-int main(int, char**)
-{
- {
- typedef std::unordered_multiset<int> C;
- typedef int P;
- P a[] =
- {
- P(1),
- P(2),
- P(3),
- P(4),
- P(1),
- P(2)
- };
- C c;
- c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
- assert(c.size() == 6);
- assert(c.count(1) == 2);
- assert(c.count(2) == 2);
- assert(c.count(3) == 1);
- assert(c.count(4) == 1);
- }
-#if TEST_STD_VER >= 11
- {
- typedef std::unordered_multiset<int, std::hash<int>,
- std::equal_to<int>, min_allocator<int>> C;
- typedef int P;
- P a[] =
- {
- P(1),
- P(2),
- P(3),
- P(4),
- P(1),
- P(2)
- };
- C c;
- c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
- assert(c.size() == 6);
- assert(c.count(1) == 2);
- assert(c.count(2) == 2);
- assert(c.count(3) == 1);
- assert(c.count(4) == 1);
- }
-#endif
+int main(int, char**) {
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_map_set_insert_range<std::unordered_multiset<int, test_hash<int>, test_equal_to<int>, Alloc>, int, Iter, Sent>(/*allow_duplicates=*/true);
+ });
+
+ static_assert(test_set_constraints_insert_range<std::unordered_multiset, int, double>());
+
+ test_set_insert_range_move_only<std::unordered_multiset>();
+
+ test_set_insert_range_exception_safety_throwing_copy<std::unordered_multiset>();
+ test_unord_set_insert_range_exception_safety_throwing_allocator<std::unordered_multiset, int>();
return 0;
}
+
diff --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/deduct.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/deduct.pass.cpp
index 912a6d8..7244887 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/deduct.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/deduct.pass.cpp
@@ -46,13 +46,38 @@
// template<class T, class Hash, class Allocator>
// unordered_multiset(initializer_list<T>, typename see below::size_type, Hash, Allocator)
// -> unordered_multiset<T, Hash, equal_to<T>, Allocator>;
+//
+// template<ranges::input_range R,
+// class Hash = hash<ranges::range_value_t<R>>,
+// class Pred = equal_to<ranges::range_value_t<R>>,
+// class Allocator = allocator<ranges::range_value_t<R>>>
+// unordered_multiset(from_range_t, R&&, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+// -> unordered_multiset<ranges::range_value_t<R>, Hash, Pred, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+// unordered_multiset(from_range_t, R&&, typename see below::size_type, Allocator)
+// -> unordered_multiset<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+// equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+// unordered_multiset(from_range_t, R&&, Allocator)
+// -> unordered_multiset<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+// equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Hash, class Allocator>
+// unordered_multiset(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+// -> unordered_multiset<ranges::range_value_t<R>, Hash,
+// equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
#include <algorithm> // is_permutation
+#include <array>
#include <cassert>
#include <climits> // INT_MAX
#include <type_traits>
#include <unordered_set>
+#include "../../../test_compare.h"
+#include "../../../test_hash.h"
#include "deduction_guides_sfinae_checks.h"
#include "test_allocator.h"
@@ -193,6 +218,57 @@ int main(int, char**)
assert(s.get_allocator().get_id() == 42);
}
+#if TEST_STD_VER >= 23
+ {
+ using Range = std::array<int, 0>;
+ using Pred = test_equal_to<int>;
+ using DefaultPred = std::equal_to<int>;
+ using Hash = test_hash<int>;
+ using DefaultHash = std::hash<int>;
+ using Alloc = test_allocator<int>;
+
+ { // (from_range, range)
+ std::unordered_multiset c(std::from_range, Range());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int>>);
+ }
+
+ { // (from_range, range, n)
+ std::unordered_multiset c(std::from_range, Range(), std::size_t());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int>>);
+ }
+
+ { // (from_range, range, n, hash)
+ std::unordered_multiset c(std::from_range, Range(), std::size_t(), Hash());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int, Hash>>);
+ }
+
+ { // (from_range, range, n, hash, pred)
+ std::unordered_multiset c(std::from_range, Range(), std::size_t(), Hash(), Pred());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int, Hash, Pred>>);
+ }
+
+ { // (from_range, range, n, hash, pred, alloc)
+ std::unordered_multiset c(std::from_range, Range(), std::size_t(), Hash(), Pred(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int, Hash, Pred, Alloc>>);
+ }
+
+ { // (from_range, range, n, alloc)
+ std::unordered_multiset c(std::from_range, Range(), std::size_t(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int, DefaultHash, DefaultPred, Alloc>>);
+ }
+
+ // TODO(LWG 2713): uncomment this test once the constructor is added.
+ { // (from_range, range, alloc)
+ //std::unordered_multiset c(std::from_range, Range(), Alloc());
+ //static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int, DefaultHash, DefaultPred, Alloc>>);
+ }
+
+ { // (from_range, range, n, hash, alloc)
+ std::unordered_multiset c(std::from_range, Range(), std::size_t(), Hash(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int, Hash, DefaultPred, Alloc>>);
+ }
+ }
+#endif
UnorderedContainerDeductionGuidesSfinaeAway<std::unordered_multiset, std::unordered_multiset<int>>();
return 0;
diff --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/from_range.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/from_range.pass.cpp
new file mode 100644
index 0000000..918e7d8
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/from_range.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<value_type> R>
+// unordered_multiset(from_range_t, R&& rg, size_type n = see below,
+// const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+// const allocator_type& a = allocator_type()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+// unordered_multiset(from_range_t, R&& rg, size_type n, const allocator_type& a)
+// : unordered_multiset(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+//
+// template<container-compatible-range<value_type> R>
+// unordered_multiset(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+// : unordered_multiset(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { } // C++23
+
+#include <array>
+#include <unordered_set>
+
+#include "../../from_range_unordered_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+ std::array input = {1, 2, 3, 3, 3, 4, 2, 1, 2};
+ auto c = std::unordered_multiset<int>(std::from_range, input);
+ assert(std::ranges::is_permutation(input, c));
+}
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+ test_unordered_set<std::unordered_multiset, int, Iter, Sent, test_hash<int>, test_equal_to<int>, Alloc>();
+ });
+ test_unordered_set_move_only<std::unordered_multiset>();
+
+ static_assert(test_set_constraints<std::unordered_set, int, double>());
+
+ test_set_exception_safety_throwing_copy<std::unordered_multiset>();
+ test_set_exception_safety_throwing_allocator<std::unordered_multiset, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/unord/unord.set/insert_iter_iter.pass.cpp
new file mode 100644
index 0000000..451ee67
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/insert_iter_iter.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <unordered_set>
+
+// template <class Value, class Hash = hash<Value>, class Pred = equal_to<Value>,
+// class Alloc = allocator<Value>>
+// class unordered_set
+
+// template <class InputIterator>
+// void insert(InputIterator first, InputIterator last);
+
+#include <unordered_set>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "min_allocator.h"
+
+int main(int, char**)
+{
+ {
+ typedef std::unordered_set<int> C;
+ typedef int P;
+ P a[] =
+ {
+ P(1),
+ P(2),
+ P(3),
+ P(4),
+ P(1),
+ P(2)
+ };
+ C c;
+ c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+ assert(c.size() == 4);
+ assert(c.count(1) == 1);
+ assert(c.count(2) == 1);
+ assert(c.count(3) == 1);
+ assert(c.count(4) == 1);
+ }
+#if TEST_STD_VER >= 11
+ {
+ typedef std::unordered_set<int, std::hash<int>,
+ std::equal_to<int>, min_allocator<int>> C;
+ typedef int P;
+ P a[] =
+ {
+ P(1),
+ P(2),
+ P(3),
+ P(4),
+ P(1),
+ P(2)
+ };
+ C c;
+ c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+ assert(c.size() == 4);
+ assert(c.count(1) == 1);
+ assert(c.count(2) == 1);
+ assert(c.count(3) == 1);
+ assert(c.count(4) == 1);
+ }
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/insert_range.pass.cpp b/libcxx/test/std/containers/unord/unord.set/insert_range.pass.cpp
index 451ee67..c6306a2 100644
--- a/libcxx/test/std/containers/unord/unord.set/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/insert_range.pass.cpp
@@ -6,67 +6,31 @@
//
//===----------------------------------------------------------------------===//
-// <unordered_set>
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
-// template <class Value, class Hash = hash<Value>, class Pred = equal_to<Value>,
-// class Alloc = allocator<Value>>
-// class unordered_set
+// <set>
-// template <class InputIterator>
-// void insert(InputIterator first, InputIterator last);
+// template<container-compatible-range<value_type> R>
+// void insert_range(R&& rg); // C++23
#include <unordered_set>
-#include <cassert>
+#include "../../insert_range_maps_sets.h"
#include "test_macros.h"
-#include "test_iterators.h"
-#include "min_allocator.h"
-int main(int, char**)
-{
- {
- typedef std::unordered_set<int> C;
- typedef int P;
- P a[] =
- {
- P(1),
- P(2),
- P(3),
- P(4),
- P(1),
- P(2)
- };
- C c;
- c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
- assert(c.size() == 4);
- assert(c.count(1) == 1);
- assert(c.count(2) == 1);
- assert(c.count(3) == 1);
- assert(c.count(4) == 1);
- }
-#if TEST_STD_VER >= 11
- {
- typedef std::unordered_set<int, std::hash<int>,
- std::equal_to<int>, min_allocator<int>> C;
- typedef int P;
- P a[] =
- {
- P(1),
- P(2),
- P(3),
- P(4),
- P(1),
- P(2)
- };
- C c;
- c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
- assert(c.size() == 4);
- assert(c.count(1) == 1);
- assert(c.count(2) == 1);
- assert(c.count(3) == 1);
- assert(c.count(4) == 1);
- }
-#endif
+int main(int, char**) {
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_map_set_insert_range<std::unordered_set<int, test_hash<int>, test_equal_to<int>, Alloc>, int, Iter, Sent>();
+ });
+
+ static_assert(test_set_constraints_insert_range<std::unordered_set, int, double>());
+
+ test_set_insert_range_move_only<std::unordered_set>();
+
+ test_set_insert_range_exception_safety_throwing_copy<std::unordered_set>();
+ test_unord_set_insert_range_exception_safety_throwing_allocator<std::unordered_set, int>();
return 0;
}
diff --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/deduct.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/deduct.pass.cpp
index 13c5416..13e42f3 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/deduct.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/deduct.pass.cpp
@@ -46,13 +46,38 @@
// template<class T, class Hash, class Allocator>
// unordered_set(initializer_list<T>, typename see below::size_type, Hash, Allocator)
// -> unordered_set<T, Hash, equal_to<T>, Allocator>;
+//
+// template<ranges::input_range R,
+// class Hash = hash<ranges::range_value_t<R>>,
+// class Pred = equal_to<ranges::range_value_t<R>>,
+// class Allocator = allocator<ranges::range_value_t<R>>>
+// unordered_set(from_range_t, R&&, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+// -> unordered_set<ranges::range_value_t<R>, Hash, Pred, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+// unordered_set(from_range_t, R&&, typename see below::size_type, Allocator)
+// -> unordered_set<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+// equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+// unordered_set(from_range_t, R&&, Allocator)
+// -> unordered_set<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+// equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Hash, class Allocator>
+// unordered_set(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+// -> unordered_set<ranges::range_value_t<R>, Hash,
+// equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
#include <algorithm> // is_permutation
+#include <array>
#include <cassert>
#include <climits> // INT_MAX
#include <type_traits>
#include <unordered_set>
+#include "../../../test_compare.h"
+#include "../../../test_hash.h"
#include "deduction_guides_sfinae_checks.h"
#include "test_allocator.h"
@@ -193,6 +218,58 @@ int main(int, char**)
assert(s.get_allocator().get_id() == 42);
}
+#if TEST_STD_VER >= 23
+ {
+ using Range = std::array<int, 0>;
+ using Pred = test_equal_to<int>;
+ using DefaultPred = std::equal_to<int>;
+ using Hash = test_hash<int>;
+ using DefaultHash = std::hash<int>;
+ using Alloc = test_allocator<int>;
+
+ { // (from_range, range)
+ std::unordered_set c(std::from_range, Range());
+ static_assert(std::is_same_v<decltype(c), std::unordered_set<int>>);
+ }
+
+ { // (from_range, range, n)
+ std::unordered_set c(std::from_range, Range(), std::size_t());
+ static_assert(std::is_same_v<decltype(c), std::unordered_set<int>>);
+ }
+
+ { // (from_range, range, n, hash)
+ std::unordered_set c(std::from_range, Range(), std::size_t(), Hash());
+ static_assert(std::is_same_v<decltype(c), std::unordered_set<int, Hash>>);
+ }
+
+ { // (from_range, range, n, hash, pred)
+ std::unordered_set c(std::from_range, Range(), std::size_t(), Hash(), Pred());
+ static_assert(std::is_same_v<decltype(c), std::unordered_set<int, Hash, Pred>>);
+ }
+
+ { // (from_range, range, n, hash, pred, alloc)
+ std::unordered_set c(std::from_range, Range(), std::size_t(), Hash(), Pred(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::unordered_set<int, Hash, Pred, Alloc>>);
+ }
+
+ { // (from_range, range, n, alloc)
+ std::unordered_set c(std::from_range, Range(), std::size_t(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::unordered_set<int, DefaultHash, DefaultPred, Alloc>>);
+ }
+
+ // TODO(LWG 2713): uncomment this test once the constructor is added.
+ { // (from_range, range, alloc)
+ //std::unordered_set c(std::from_range, Range(), Alloc());
+ //static_assert(std::is_same_v<decltype(c), std::unordered_set<int, DefaultHash, DefaultPred, Alloc>>);
+ }
+
+ { // (from_range, range, n, hash, alloc)
+ std::unordered_set c(std::from_range, Range(), std::size_t(), Hash(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::unordered_set<int, Hash, DefaultPred, Alloc>>);
+ }
+ }
+#endif
+
UnorderedContainerDeductionGuidesSfinaeAway<std::unordered_set, std::unordered_set<int>>();
return 0;
diff --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/from_range.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/from_range.pass.cpp
new file mode 100644
index 0000000..58178194
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/from_range.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<value_type> R>
+// unordered_set(from_range_t, R&& rg, size_type n = see below,
+// const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+// const allocator_type& a = allocator_type()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+// unordered_set(from_range_t, R&& rg, size_type n, const allocator_type& a)
+// : unordered_set(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+//
+// template<container-compatible-range<value_type> R>
+// unordered_set(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+// : unordered_set(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { } // C++23
+
+#include <array>
+#include <unordered_set>
+
+#include "../../from_range_unordered_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+ using T = KeyValue;
+
+ std::array input = {
+ T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}
+ };
+ std::array expected = {
+ T{1, 'a'}, T{2, 'b'}, T{3, 'c'}, T{4, 'a'}
+ };
+ auto c = std::unordered_set<T>(std::from_range, input);
+ assert(std::ranges::is_permutation(expected, c));
+}
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+ test_unordered_set<std::unordered_set, int, Iter, Sent, test_hash<int>, test_equal_to<int>, Alloc>();
+ });
+ test_unordered_set_move_only<std::unordered_set>();
+ test_duplicates();
+
+ static_assert(test_set_constraints<std::unordered_set, int, double>());
+
+ test_set_exception_safety_throwing_copy<std::unordered_set>();
+ test_set_exception_safety_throwing_allocator<std::unordered_set, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/support/deduction_guides_sfinae_checks.h b/libcxx/test/support/deduction_guides_sfinae_checks.h
index dcc41aa..8d13658 100644
--- a/libcxx/test/support/deduction_guides_sfinae_checks.h
+++ b/libcxx/test/support/deduction_guides_sfinae_checks.h
@@ -161,8 +161,8 @@ constexpr void ContainerAdaptorDeductionGuidesSfinaeAway() {
// - "bad" input iterators (that is, a type not qualifying as an input
// iterator);
// - a bad allocator;
-// - an allocator in place of a comparator.
-
+// - an allocator in place of a comparator;
+// - a range not satisfying the `input_range` concept.
template<template<typename ...> class Container, typename InstantiatedContainer>
constexpr void AssociativeContainerDeductionGuidesSfinaeAway() {
using ValueType = typename InstantiatedContainer::value_type;
@@ -224,6 +224,42 @@ constexpr void AssociativeContainerDeductionGuidesSfinaeAway() {
// Note: (init_list, BAD_alloc) is interpreted as (init_list, comp) instead
// and fails upon instantiation. There is no requirement to SFINAE away bad
// comparators.
+
+#if TEST_STD_VER >= 23
+ using Range = RangeT<ValueType>;
+ using BadRange = BadRangeT<ValueType>;
+
+ // (from_range, range)
+ //
+ // Can deduce from (from_range, range)
+ static_assert(!SFINAEs_away<Container, std::from_range_t, Range>);
+ // Cannot deduce from (from_range, BAD_range)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange>);
+
+ // (from_range, range, comp)
+ //
+ // Can deduce from (from_range, _range, comp)
+ static_assert(!SFINAEs_away<Container, std::from_range_t, Range, Comp>);
+ // Cannot deduce from (from_range, BAD_range, comp)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, Comp>);
+
+ // (from_range, range, comp, alloc)
+ //
+ // Can deduce from (from_range, range, comp, alloc)
+ static_assert(!SFINAEs_away<Container, std::from_range_t, Range, Comp, Alloc>);
+ // Cannot deduce from (from_range, BAD_range, comp, alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, Comp, Alloc>);
+ // Cannot deduce from (from_range, range, comp, BAD_alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, Range, Comp, BadAlloc>);
+
+ // (from_range, range, alloc)
+ //
+ // Can deduce from (from_range, range, alloc)
+ static_assert(!SFINAEs_away<Container, std::from_range_t, Range, Alloc>);
+ // Cannot deduce from (from_range, BAD_range, alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, Alloc>);
+ // Note: (from_range, range, BAD_alloc) is interpreted as (from_range, range, comp) instead.
+#endif
}
// For unordered containers the deduction guides should be SFINAE'd away when
@@ -233,7 +269,8 @@ constexpr void AssociativeContainerDeductionGuidesSfinaeAway() {
// - a bad allocator;
// - a bad hash functor (an integral type in place of a hash);
// - an allocator in place of a hash functor;
-// - an allocator in place of a predicate.
+// - an allocator in place of a predicate;
+// - a range not satisfying the `input_range` concept.
template<template<typename ...> class Container, typename InstantiatedContainer>
constexpr void UnorderedContainerDeductionGuidesSfinaeAway() {
using ValueType = typename InstantiatedContainer::value_type;
@@ -243,7 +280,7 @@ constexpr void UnorderedContainerDeductionGuidesSfinaeAway() {
using Iter = ValueType*;
using InitList = std::initializer_list<ValueType>;
- using BadHash = int;
+ using BadHash = short;
struct BadAlloc {};
// The only requirement in the Standard is that integral types cannot be
// considered input iterators, beyond that it is unspecified.
@@ -298,8 +335,7 @@ constexpr void UnorderedContainerDeductionGuidesSfinaeAway() {
LIBCPP_STATIC_ASSERT(SFINAEs_away<Container, OutputIter, OutputIter,
std::size_t, Hash, Pred, Alloc>);
// Cannot deduce from (iter, iter, buckets, BAD_hash, pred, alloc)
- static_assert(
- SFINAEs_away<Container, Iter, Iter, std::size_t, BadHash, Pred, Alloc>);
+ static_assert(SFINAEs_away<Container, Iter, Iter, std::size_t, BadHash, Pred, Alloc>);
// Cannot deduce from (iter, iter, buckets, ALLOC_as_hash, pred, alloc)
static_assert(
SFINAEs_away<Container, Iter, Iter, std::size_t, AllocAsHash, Pred, Alloc>);
@@ -337,8 +373,7 @@ constexpr void UnorderedContainerDeductionGuidesSfinaeAway() {
// Cannot deduce from (iter, iter, buckets, BAD_hash, alloc)
static_assert(SFINAEs_away<Container, Iter, Iter, std::size_t, BadHash, Alloc>);
// Cannot deduce from (iter, iter, buckets, ALLOC_as_hash, alloc)
- static_assert(
- SFINAEs_away<Container, Iter, Iter, std::size_t, AllocAsHash, Alloc>);
+ static_assert(SFINAEs_away<Container, Iter, Iter, std::size_t, AllocAsHash, Alloc>);
// Note: (iter, iter, buckets, hash, BAD_alloc) is interpreted as (iter, iter,
// buckets, hash, pred), which is valid because there are no requirements for
// the predicate.
@@ -391,6 +426,87 @@ constexpr void UnorderedContainerDeductionGuidesSfinaeAway() {
//
// Cannot deduce from (init_list, BAD_alloc)
static_assert(SFINAEs_away<Container, InitList, BadAlloc>);
+
+#if TEST_STD_VER >= 23
+ using Range = RangeT<ValueType>;
+ using BadRange = BadRangeT<ValueType>;
+
+ // (from_range, range)
+ //
+ // Can deduce from (from_range, range)
+ static_assert(!SFINAEs_away<Container, std::from_range_t, Range>);
+ // Cannot deduce from (from_range, BAD_range)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange>);
+
+ // (from_range, range, buckets)
+ //
+ // Can deduce from (from_range, range, buckets)
+ static_assert(!SFINAEs_away<Container, std::from_range_t, Range, std::size_t>);
+ // Cannot deduce from (from_range, BAD_range, buckets)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, std::size_t>);
+
+ // (from_range, range, buckets, hash)
+ //
+ // Can deduce from (from_range, range, buckets, hash)
+ static_assert(!SFINAEs_away<Container, std::from_range_t, Range, std::size_t, Hash>);
+ // Cannot deduce from (from_range, BAD_range, buckets, hash)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, std::size_t, Hash>);
+ // Cannot deduce from (from_range, range, buckets, BAD_hash)
+ static_assert(SFINAEs_away<Container, std::from_range_t, Range, std::size_t, BadHash>);
+
+ // (from_range, range, buckets, hash, pred)
+ //
+ // Can deduce from (from_range, range, buckets, hash, pred)
+ static_assert(!SFINAEs_away<Container, std::from_range_t, Range, std::size_t, Hash, Pred>);
+ // Cannot deduce from (from_range, BAD_range, buckets, hash, pred)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, std::size_t, Hash, Pred>);
+ // Cannot deduce from (from_range, range, buckets, BAD_hash, pred)
+ static_assert(SFINAEs_away<Container, std::from_range_t, Range, std::size_t, BadHash, Pred>);
+
+ // (from_range, range, buckets, hash, pred, alloc)
+ //
+ // Can deduce from (from_range, range, buckets, hash, pred, alloc)
+ static_assert(!SFINAEs_away<Container, std::from_range_t, Range, std::size_t, Hash, Pred, Alloc>);
+ // Cannot deduce from (from_range, BAD_range, buckets, hash, pred, alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, std::size_t, Hash, Pred, Alloc>);
+ // Cannot deduce from (from_range, range, buckets, BAD_hash, pred, alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, Range, std::size_t, BadHash, Pred, Alloc>);
+ // Cannot deduce from (from_range, range, buckets, hash, pred, BAD_alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, Range, std::size_t, Hash, Pred, BadAlloc>);
+
+ // (from_range, range, buckets, alloc)
+ //
+ // Can deduce from (from_range, range, buckets, alloc)
+ static_assert(!SFINAEs_away<Container, std::from_range_t, Range, std::size_t, Alloc>);
+ // Cannot deduce from (from_range, BAD_range, buckets, alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, std::size_t, Alloc>);
+ // Note: (from_range, range, buckets, BAD_alloc) is interpreted as (from_range, range, buckets, hash), which is valid
+ // because the only requirement for the hash parameter is that it's not integral.
+
+ // (from_range, range, alloc)
+ //
+ // Can deduce from (from_range, range, alloc)
+ // TODO(LWG 2713): uncomment this test once the constructor is added.
+ // static_assert(!SFINAEs_away<Container, std::from_range_t, Range, Alloc>);
+ // Cannot deduce from (from_range, BAD_range, alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, Alloc>);
+ // Cannot deduce from (from_range, range, BAD_alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, Range, BadAlloc>);
+
+ // (from_range, range, buckets, hash, alloc)
+ //
+ // Can deduce from (from_range, range, buckets, hash, alloc)
+ static_assert(!SFINAEs_away<Container, std::from_range_t, Range, std::size_t, Hash, Alloc>);
+ // Cannot deduce from (from_range, BAD_range, buckets, hash, alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, std::size_t, Hash, Alloc>);
+ // Cannot deduce from (from_range, range, buckets, BAD_hash, alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, Range, std::size_t, BadHash, Alloc>);
+ // Cannot deduce from (from_range, range, buckets, ALLOC_as_hash, alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, Range, std::size_t, AllocAsHash, Alloc>);
+ // Cannot deduce from (from_range, range, buckets, hash, BAD_alloc)
+ // Note: (from_range, range, buckets, hash, BAD_alloc) is interpreted as (from_range, range, buckets, hash, pred),
+ // which is valid because the only requirement for the predicate parameter is that it does not resemble an allocator.
+#endif
}
#endif // TEST_SUPPORT_DEDUCTION_GUIDES_SFINAE_CHECKS_H