diff options
author | varconst <varconsteq@gmail.com> | 2023-07-10 17:16:56 -0700 |
---|---|---|
committer | varconst <varconsteq@gmail.com> | 2023-07-18 11:01:10 -0700 |
commit | ef70fe4d264d20abdd8476605650479a96a62071 (patch) | |
tree | 52f4102af9372ee316ed232f4223967b065b7e16 /libcxx | |
parent | 5267ed05bc4612e91409d63b4dbc4e01751acb75 (diff) | |
download | llvm-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')
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 |