diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index f82705a800b..369957a3310 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -157,6 +157,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/experimental/unordered_set ${CMAKE_CURRENT_LIST_DIR}/inc/experimental/vector ${CMAKE_CURRENT_LIST_DIR}/inc/filesystem + ${CMAKE_CURRENT_LIST_DIR}/inc/flat_map ${CMAKE_CURRENT_LIST_DIR}/inc/format ${CMAKE_CURRENT_LIST_DIR}/inc/forward_list ${CMAKE_CURRENT_LIST_DIR}/inc/fstream diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index 268f3a862f6..0ef854b2036 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -92,6 +92,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/flat_map b/stl/inc/flat_map new file mode 100644 index 00000000000..52e35f8a7bf --- /dev/null +++ b/stl/inc/flat_map @@ -0,0 +1,1163 @@ +// flat_map standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef _FLAT_MAP_ +#define _FLAT_MAP_ +#include +#if _STL_COMPILER_PREPROCESSOR +#if !_HAS_CXX23 || !defined(__cpp_lib_concepts) // TRANSITION, GH-395 +_EMIT_STL_WARNING(STL4038, "The contents of are available only with C++23 or later."); +#else // ^^^ not supported / supported language mode vvv + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_STD_BEGIN + +template + requires same_as<_FlatMap_Key, typename _FlatMap_KeyContainer::value_type> + && same_as<_FlatMap_T, typename _FlatMap_MappedContainer::value_type> +class _Flat_Map_Base; + +template , class _KeyContainer = vector<_Key>, + class _MappedContainer = vector<_Mapped>> +class flat_map; + +template , class _KeyContainer = vector<_Key>, + class _MappedContainer = vector<_Mapped>> +class flat_multimap; + +struct sorted_unique_t { + explicit sorted_unique_t() = default; +}; +inline constexpr sorted_unique_t sorted_unique{}; + +struct sorted_equivalent_t { + explicit sorted_equivalent_t() = default; +}; +inline constexpr sorted_equivalent_t sorted_equivalent{}; + +template +concept _Valid_Allocator_for_flat_map = + uses_allocator_v<_Key_container, _Alloc> && uses_allocator_v<_Mapped_container, _Alloc>; + +template +concept _Valid_Compare_for_container = is_invocable_v; + +template +struct _Flat_value_compare { + struct value_compare { + public: + bool operator()(pair _X, pair _Y) const { + return _Key_compare_for_val(_X.first, _Y.first); + } + + value_compare(_Key_compare _Comp) : _Key_compare_for_val(_Comp) {} + + private: + _Key_compare _Key_compare_for_val; + }; +}; + +template +struct _Flat_Container { + struct container { + _Key_container keys; + _Mapped_container values; + }; +}; + +template +struct _NODISCARD _Clear_flat_map_scope_guard { + _Ty* _Clearable; + _Clear_flat_map_scope_guard(_Ty* _Clearable) : _Clearable(_Clearable) {} + + ~_Clear_flat_map_scope_guard() { + if (_Clearable) { + _Clearable->clear(); + } + } +}; + +// Implementation + +template +class _Flat_map_iterator_Impl { +public: + using _Key_iterator_t = typename _KeyContainer::const_iterator; + using _Mapped_iterator_t = + conditional_t<_IsConst, typename _MappedContainer::const_iterator, typename _MappedContainer::iterator>; + class type { + public: + template + requires same_as<_FlatMap_Key, typename _FlatMap_KeyContainer::value_type> + && same_as<_FlatMap_T, typename _FlatMap_MappedContainer::value_type> + friend class _Flat_Map_Base; + type() = default; + type(_Key_iterator_t _Key_it, _Mapped_iterator_t _Mapped_it) : _Key_it(_Key_it), _Mapped_it(_Mapped_it) {} + + using iterator_category = input_iterator_tag; + using iterator_concept = random_access_iterator_tag; + using difference_type = ptrdiff_t; + using value_type = pair, iter_value_t<_Mapped_iterator_t>>; + using reference = pair, iter_reference_t<_Mapped_iterator_t>>; + + private: + class _Arrow_proxy { + public: + explicit _Arrow_proxy(const reference& _Rx) noexcept : _Ref{_Rx} {} + + const reference* operator->() const noexcept { + return _STD addressof(_Ref); + } + + private: + reference _Ref; + }; + + public: + using pointer = _Arrow_proxy; + + reference operator*() const { + return reference{*_Key_it, *_Mapped_it}; + } + + pointer operator->() const { + return pointer{*(*this)}; + } + + type& operator++() { + ++_Key_it; + ++_Mapped_it; + return *this; + } + + type operator++(int) { + type _Tmp = *this; + ++*this; + return _Tmp; + } + + bool operator==(const type& _Right) const { + return _Key_it == _Right._Key_it; + } + + auto operator<=>(const type& _Right) const { + return _Key_it <=> _Right._Key_it; + } + + type& operator--() { + --_Key_it; + --_Mapped_it; + return *this; + } + + type operator--(int) { + type _Tmp = *this; + --*this; + return _Tmp; + } + + type& operator+=(difference_type _Off) { + _Key_it += _Off; + _Mapped_it += _Off; + return *this; + } + + type& operator-=(difference_type _Off) { + _Key_it -= _Off; + _Mapped_it -= _Off; + return *this; + } + + type operator+(difference_type _Off) const { + type _Tmp = *this; + return _Tmp += _Off; + } + + type operator-(difference_type _Off) const { + type _Tmp = *this; + return _Tmp -= _Off; + } + + reference operator[](difference_type _Off) const { + return *(*this + _Off); + } + + difference_type operator-(const type& _Right) const { + return _Key_it - _Right._Key_it; + } + + friend type operator+(difference_type _Off, const type& _Right) { + return _Right + _Off; + } + + operator typename _Flat_map_iterator_Impl<_KeyContainer, _MappedContainer, true>::type() const + requires (!_IsConst) + { + return typename _Flat_map_iterator_Impl<_KeyContainer, _MappedContainer, true>::type{_Key_it, _Mapped_it}; + } + + private: + _Key_iterator_t _Key_it; + _Mapped_iterator_t _Mapped_it; + }; + + static_assert(swappable); +}; + +_EXPORT_STD +template + requires same_as<_FlatMap_Key, typename _FlatMap_KeyContainer::value_type> + && same_as<_FlatMap_T, typename _FlatMap_MappedContainer::value_type> +class _Flat_Map_Base { +private: + using _Sorted_t = conditional_t<_Is_Multi, sorted_equivalent_t, sorted_unique_t>; + +public: + using key_type = _FlatMap_Key; + using mapped_type = _FlatMap_T; + using value_type = pair; + using key_compare = _FlatMap_Compare; + using reference = pair; + using const_reference = pair; + using size_type = size_t; + using difference_type = ptrdiff_t; + using key_container_type = _FlatMap_KeyContainer; + using mapped_container_type = _FlatMap_MappedContainer; + using iterator = _Flat_map_iterator_Impl::type; + using const_iterator = _Flat_map_iterator_Impl::type; + using reverse_iterator = _STD reverse_iterator; + using const_reverse_iterator = _STD reverse_iterator; + + static_assert(random_access_iterator); + static_assert(convertible_to); + + using value_compare = _Flat_value_compare::value_compare; + using containers = _Flat_Container::container; + +public: + // [flat.map.cons] Constructors + explicit _Flat_Map_Base(const key_compare& _Comp) : _Key_compare(_Comp), _Data() {} + _Flat_Map_Base() : _Flat_Map_Base(key_compare()) {} + + template <_Valid_Allocator_for_flat_map Allocator> + explicit _Flat_Map_Base(const Allocator& _Alloc) : _Flat_Map_Base(key_compare(), _Alloc) {} + + template <_Valid_Allocator_for_flat_map Allocator> + explicit _Flat_Map_Base(const key_compare& _Comp, const Allocator& _Alloc) + : _Key_compare(_Comp), _Data{.keys = _STD make_obj_using_allocator(_Alloc), + .values = _STD make_obj_using_allocator(_Alloc)} {} + + _Flat_Map_Base( + key_container_type _Key_cont, mapped_container_type _Mapped_cont, const key_compare& _Comp = key_compare()) + : _Flat_Map_Base(_Sorted_t(), _Key_cont, _Mapped_cont, _Comp) { + _Sort(); + if constexpr (!_Is_Multi) { + _Dedup(); + } + } + + template <_Valid_Allocator_for_flat_map Allocator> + _Flat_Map_Base( + const key_container_type& _Key_cont, const mapped_container_type& _Mapped_cont, const Allocator& _Alloc) + : _Flat_Map_Base(_Sorted_t(), _Key_cont, _Mapped_cont, _Alloc) { + _Sort(); + if constexpr (!_Is_Multi) { + _Dedup(); + } + } + + template <_Valid_Allocator_for_flat_map Allocator> + _Flat_Map_Base(const key_container_type& _Key_cont, const mapped_container_type& _Mapped_cont, + const key_compare& _Comp, const Allocator& _Alloc) + : _Flat_Map_Base(_Sorted_t(), _Key_cont, _Mapped_cont, _Comp, _Alloc) { + _Sort(); + if constexpr (!_Is_Multi) { + _Dedup(); + } + } + + _Flat_Map_Base(_Sorted_t, key_container_type _Key_cont, mapped_container_type _Mapped_cont, + const key_compare& _Comp = key_compare()) + : _Key_compare(_Comp), _Data{.keys = _STD move(_Key_cont), .values = _STD move(_Mapped_cont)} {} + + template <_Valid_Allocator_for_flat_map Allocator> + _Flat_Map_Base(_Sorted_t, const key_container_type& _Key_cont, const mapped_container_type& _Mapped_cont, + const Allocator& _Alloc) + : _Key_compare(key_compare()), + _Data{.keys = _STD make_obj_using_allocator(_Alloc, _Key_cont), + .values = _STD make_obj_using_allocator(_Alloc, _Mapped_cont)} {} + + template <_Valid_Allocator_for_flat_map Allocator> + _Flat_Map_Base(_Sorted_t, const key_container_type& _Key_cont, const mapped_container_type& _Mapped_cont, + const key_compare& _Comp, const Allocator& _Alloc) + : _Key_compare(_Comp), + _Data{.keys = _STD make_obj_using_allocator(_Alloc, _Key_cont), + .values = _STD make_obj_using_allocator(_Alloc, _Mapped_cont)} {} + + template + requires _Is_iterator_v<_InputIterator> + _Flat_Map_Base(_InputIterator _First, _InputIterator _Last, const key_compare& _Comp = key_compare()) + : _Flat_Map_Base(_Comp) { + insert(_First, _Last); + } + + template Allocator> + requires _Is_iterator_v<_InputIterator> + _Flat_Map_Base(_InputIterator _First, _InputIterator _Last, const key_compare& _Comp, const Allocator& _Alloc) + : _Flat_Map_Base(_Comp, _Alloc) { + insert(_First, _Last); + } + + template <_Container_compatible_range R> + _Flat_Map_Base(from_range_t _From_range, R&& _Range) + : _Flat_Map_Base(_From_range, _STD forward(_Range), key_compare()) {} + + template <_Container_compatible_range R, + _Valid_Allocator_for_flat_map Allocator> + _Flat_Map_Base(from_range_t _From_range, R&& _Range, const Allocator& _Alloc) + : _Flat_Map_Base(_From_range, _STD forward(_Range), key_compare(), _Alloc) {} + + template <_Container_compatible_range R> + _Flat_Map_Base(from_range_t, R&& _Range, const key_compare& _Comp) : _Flat_Map_Base(_Comp) { + insert_range(_STD forward(_Range)); + } + + template <_Container_compatible_range R, + _Valid_Allocator_for_flat_map Allocator> + _Flat_Map_Base(from_range_t, R&& _Range, const key_compare& _Comp, const Allocator& _Alloc) + : _Flat_Map_Base(_Comp, _Alloc) { + insert_range(_STD forward(_Range)); + } + + template + requires _Is_iterator_v<_InputIterator> + _Flat_Map_Base(_Sorted_t _S, _InputIterator _First, _InputIterator _Last, const key_compare& _Comp = key_compare()) + : _Flat_Map_Base(_Comp) { + insert(_S, _First, _Last); + } + + template Allocator> + requires _Is_iterator_v<_InputIterator> + _Flat_Map_Base( + _Sorted_t _S, _InputIterator _First, _InputIterator _Last, const key_compare& _Comp, const Allocator& _Alloc) + : _Flat_Map_Base(_Comp, _Alloc) { + insert(_S, _First, _Last); + } + + template Allocator> + requires _Is_iterator_v<_InputIterator> + _Flat_Map_Base(_Sorted_t _S, _InputIterator _First, _InputIterator _Last, const Allocator& _Alloc) + : _Flat_Map_Base(_S, _First, _Last, key_compare(), _Alloc) {} + + _Flat_Map_Base(initializer_list _I, const key_compare& _Comp = key_compare()) + : _Flat_Map_Base(_I.begin(), _I.end(), _Comp) {} + + template <_Valid_Allocator_for_flat_map Allocator> + _Flat_Map_Base(initializer_list _I, const key_compare& _Comp, const Allocator& _Alloc) + : _Flat_Map_Base(_I.begin(), _I.end(), _Comp, _Alloc) {} + + template <_Valid_Allocator_for_flat_map Allocator> + _Flat_Map_Base(initializer_list _I, const Allocator& _Alloc) + : _Flat_Map_Base(_I, key_compare(), _Alloc) {} + + _Flat_Map_Base(_Sorted_t _S, initializer_list _I, const key_compare& _Comp = key_compare()) + : _Flat_Map_Base(_S, _I.begin(), _I.end(), _Comp) {} + + template <_Valid_Allocator_for_flat_map Allocator> + _Flat_Map_Base(_Sorted_t _S, initializer_list _I, const key_compare& _Comp, const Allocator& _Alloc) + : _Flat_Map_Base(_S, _I.begin(), _I.end(), _Comp, _Alloc) {} + + template <_Valid_Allocator_for_flat_map Allocator> + _Flat_Map_Base(_Sorted_t _S, initializer_list _I, const Allocator& _Alloc) + : _Flat_Map_Base(_S, _I, key_compare(), _Alloc) {} + + // Copy constructors + _Flat_Map_Base(const _Derived& _Other) : _Key_compare(_Other._Key_compare), _Data(_Other._Data) {} + + template <_Valid_Allocator_for_flat_map _Allocator> + _Flat_Map_Base(const _Derived& _Other, const _Allocator& _Alloc) + : _Key_compare(_Other._Key_compare), + _Data{.keys = _STD make_obj_using_allocator(_Alloc, _Other._Data.keys), + .values = _STD make_obj_using_allocator(_Alloc, _Other._Data.values)} {} + + // Move constructors + _Flat_Map_Base(_Derived&& _Other) noexcept(_STD is_nothrow_move_constructible_v + && _STD is_nothrow_move_constructible_v + && _STD is_nothrow_move_constructible_v) + : _Key_compare(move(_Other._Key_compare)), _Data(move(_Other).extract()) {} + + template <_Valid_Allocator_for_flat_map _Allocator> + _Flat_Map_Base(_Derived&& _Other, const _Allocator& _Alloc) noexcept( + _STD is_nothrow_move_constructible_v && _STD is_nothrow_move_constructible_v + && _STD is_nothrow_move_constructible_v) + : _Key_compare(move(_Other._Key_compare)), + _Data{.keys = _STD make_obj_using_allocator(_Alloc, move(_Other._Data.keys)), + .values = _STD make_obj_using_allocator(_Alloc, move(_Other._Data.values))} {} + + _Derived& operator=(initializer_list _I) { + clear(); + insert(_I.begin(), _I.end()); + return static_cast<_Derived&>(*this); // Use "deducing this" when it is supported + } + + // [container.reqmts] iterators + _NODISCARD iterator begin() noexcept { + return iterator{_Data.keys.cbegin(), _Data.values.begin()}; + } + + _NODISCARD const_iterator begin() const noexcept { + return const_iterator{_Data.keys.cbegin(), _Data.values.begin()}; + } + + _NODISCARD iterator end() noexcept { + return iterator{_Data.keys.cend(), _Data.values.end()}; + } + + _NODISCARD const_iterator end() const noexcept { + return const_iterator{_Data.keys.cend(), _Data.values.end()}; + } + + _NODISCARD reverse_iterator rbegin() noexcept { + return _STD make_reverse_iterator(end()); + } + + _NODISCARD const_reverse_iterator rbegin() const noexcept { + return _STD make_reverse_iterator(end()); + } + + _NODISCARD reverse_iterator rend() noexcept { + return _STD make_reverse_iterator(begin()); + } + + _NODISCARD const_reverse_iterator rend() const noexcept { + return _STD make_reverse_iterator(begin()); + } + + _NODISCARD const_iterator cbegin() const noexcept { + return const_iterator{_Data.keys.cbegin(), _Data.values.cbegin()}; + } + + _NODISCARD const_iterator cend() const noexcept { + return const_iterator{_Data.keys.cend(), _Data.values.cend()}; + } + + _NODISCARD const_reverse_iterator crbegin() const noexcept { + return _STD make_reverse_iterator(cend()); + } + + _NODISCARD const_reverse_iterator crend() const noexcept { + return _STD make_reverse_iterator(cbegin()); + } + + void swap(_Derived& _Other) noexcept { + _RANGES swap(_Key_compare, _Other._Key_compare); + _RANGES swap(_Data.keys, _Other._Data.keys); + _RANGES swap(_Data.values, _Other._Data.values); + } + + // [container.reqmts] clear + void clear() noexcept { + _Data.keys.clear(); + _Data.values.clear(); + } + + // [flat.map.capacity] Capacity + _NODISCARD_EMPTY_MEMBER bool empty() const noexcept { + return _Data.keys.empty(); + } + + _NODISCARD size_type size() const noexcept { + return _Data.keys.size(); + } + + _NODISCARD size_type max_size() const noexcept { + return _STD min(_Data.keys.max_size(), _Data.values.max_size()); + } + + // [flat.map.access] Access + template + _NODISCARD mapped_type& operator[](_K&& _Key_val) + requires same_as, key_type> + || (_Is_transparent_v && constructible_from) + { + return try_emplace(_STD forward<_K>(_Key_val)).first->second; + } + + _NODISCARD mapped_type& at(const key_type& _Key_val) { + return _At(_Key_val); + } + + template + _NODISCARD const mapped_type& at(const _K& _Key_val) const + requires _Is_transparent_v + { + return _At(_Key_val); + } + + // [flat.map.modifiers] Modifiers + template + pair emplace(_Args_t&&... _Args) + requires is_constructible_v + { + value_type _Val(_STD forward<_Args_t>(_Args)...); + return try_emplace(_STD move(_Val.first), _STD move(_Val.second)); + } + + template + iterator emplace_hint(const_iterator _Position, _Args_t&&... _Args) + requires is_constructible_v + { + value_type _Val(_STD forward<_Args_t>(_Args)...); + + return _Emplace_hint(_Position, _STD move(_Val.first), _STD move(_Val.second)); + } + + template + pair insert(_V&& _X) + requires (same_as, value_type> || constructible_from) + { + return emplace(_STD forward<_V>(_X)); + } + + template + iterator insert(const_iterator _Position, _V&& _X) + requires (same_as, value_type> || constructible_from) + { + return emplace_hint(_Position, _STD forward<_V>(_X)); + } + + template + requires _Is_iterator_v<_InputIterator> + void insert(_InputIterator _First, _InputIterator _Last) { + _Insert_range(_First, _Last); + } + + template + requires _Is_iterator_v<_InputIterator> + void insert(_Sorted_t, _InputIterator _First, _InputIterator _Last) { + _Insert_range(_First, _Last); + } + + template <_Container_compatible_range R> + void insert_range(R&& _Range) { + insert(_RANGES begin(_Range), _RANGES end(_Range)); + } + + void insert(initializer_list _I) { + insert(_I.begin(), _I.end()); + } + + void insert(_Sorted_t _S, initializer_list _I) { + insert(_S, _I.begin(), _I.end()); + } + + template + pair try_emplace(_Key_constructible_t&& _Key_constructible, _Args_t&&... _Args) + requires constructible_from + && (same_as, key_type> + || (constructible_from && _Is_transparent_v + && !convertible_to<_Key_constructible_t &&, const_iterator> + && !convertible_to<_Key_constructible_t &&, iterator>) ) + { + auto _Key_It = _STD lower_bound(_Data.keys.begin(), _Data.keys.end(), _Key_constructible, _Key_compare); + if (_Key_It != _Data.keys.end() + && _Key_equal(*_Key_It, _STD forward<_Key_constructible_t>(_Key_constructible))) { + // Already exists + return {begin() + _STD distance(_Data.keys.begin(), _Key_It), false}; + } else { + // Need to insert + auto _Index = _STD distance(_Data.keys.begin(), _Key_It); + _Insert_exact(cbegin() + _Index, key_type{_STD forward<_Key_constructible_t>(_Key_constructible)}, + mapped_type{_STD forward<_Args_t>(_Args)...}); + return {begin() + _Index, true}; + } + } + + template + iterator try_emplace(const_iterator _Position, _K&& _Key_val, _Args_t&&... _Args) + requires constructible_from + && (same_as, key_type> + || (constructible_from && _Is_transparent_v) ) + { + return _Emplace_hint(_Position, _STD forward<_K>(_Key_val), _STD forward<_Args_t>(_Args)...); + } + + template + pair insert_or_assign(_K&& _Key_val, _M&& _Obj) + requires assignable_from && constructible_from + && (same_as, key_type> + || (constructible_from && _Is_transparent_v) ) + { + auto _Res = try_emplace(_STD forward<_K>(_Key_val), _STD forward<_M>(_Obj)); + if (_Res.second) { + // Insertion took place + return _Res; + } else { + // Already exists + *(_Res.first._Mapped_it) = _STD forward<_M>(_Obj); + return _Res; + } + } + + template + iterator insert_or_assign(const_iterator _Position, _K&& _Key_val, _M&& _Obj) + requires assignable_from && constructible_from + && (same_as, key_type> + || (constructible_from && _Is_transparent_v) ) + { + return _Emplace_hint(_Position, _STD forward<_K>(_Key_val), _STD forward<_M>(_Obj)); + } + + iterator erase(iterator _Position) { + return erase(static_cast(_Position)); + } + + iterator erase(const_iterator _Position) { + _Clear_flat_map_scope_guard _Guard{this}; + auto _Key_it = _Data.keys.erase(_Position._Key_it); + auto _Val_it = _Data.values.erase(_Position._Mapped_it); + _Guard._Clearable = nullptr; + return iterator{_STD move(_Key_it), _STD move(_Val_it)}; + } + + iterator erase(const_iterator _First, const_iterator _Last) { + _Clear_flat_map_scope_guard _Guard{this}; + auto _Key_it = _Data.keys.erase(_First._Key_it, _Last._Key_it); + auto _Val_it = _Data.values.erase(_First._Mapped_it, _Last._Mapped_it); + _Guard._Clearable = nullptr; + return iterator{_STD move(_Key_it), _STD move(_Val_it)}; + } + + template + size_type erase(_K&& _Key_val) + requires convertible_to<_K&&, const key_type&> + || (_Is_transparent_v && !convertible_to<_K &&, iterator> + && !convertible_to<_K &&, const_iterator>) + { + const_iterator _Pos_begin = lower_bound(_STD forward<_K>(_Key_val)); + const_iterator _Pos_end = upper_bound(_STD forward<_K>(_Key_val)); + size_type _Count = _Pos_end - _Pos_begin; + erase(_Pos_begin, _Pos_end); + return _Count; + } + + containers extract() && { + _Clear_flat_map_scope_guard _Guard{this}; + return _STD move(_Data); + } + + void replace(key_container_type&& _Key_cont, mapped_container_type&& _Mapped_cont) { + _Clear_flat_map_scope_guard _Guard{this}; + _Data.keys = _STD move(_Key_cont); + _Data.values = _STD move(_Mapped_cont); + _Guard._Clearable = nullptr; + } + + // observers + key_compare key_comp() const { + return _Key_compare; + } + + value_compare value_comp() const { + return value_compare(_Key_compare); + } + + const key_container_type& keys() const noexcept { + return _Data.keys; + } + + const mapped_container_type& values() const noexcept { + return _Data.values; + } + + // map operations + iterator find(const key_type& _X) { + return _Find(_X); + } + + template + iterator find(const _K& _X) + requires _Is_transparent_v + { + return _Find(_X); + } + + const_iterator find(const key_type& _X) const { + return _Find(_X); + } + + template + const_iterator find(const _K& _X) const + requires _Is_transparent_v + { + return _Find(_X); + } + + size_type count(const key_type& _X) const { + return _Count(_X); + } + + template + size_type count(const _K& _X) const + requires _Is_transparent_v + { + return _Count(_X); + } + + bool contains(const key_type& _X) const { + return _Contains(_X); + } + + template + bool contains(const _K& _X) const + requires _Is_transparent_v + { + return _Contains(_X); + } + + iterator lower_bound(const key_type& _X) { + return _Lower_bound(_X); + } + + template + iterator lower_bound(const _K& _X) + requires _Is_transparent_v + { + return _Lower_bound(_X); + } + + const_iterator lower_bound(const key_type& _X) const { + return _Lower_bound(_X); + } + + template + const_iterator lower_bound(const _K& _X) const + requires _Is_transparent_v + { + return _Lower_bound(_X); + } + + iterator upper_bound(const key_type& _X) { + return _Upper_bound(_X); + } + + template + iterator upper_bound(const _K& _X) + requires _Is_transparent_v + { + return _Upper_bound(_X); + } + + const_iterator upper_bound(const key_type& _X) const { + return _Upper_bound(_X); + } + + template + const_iterator upper_bound(const _K& _X) const + requires _Is_transparent_v + { + return _Upper_bound(_X); + } + + pair equal_range(const key_type& _X) { + return _Equal_range(_X); + } + + template + pair equal_range(const _K& _X) + requires _Is_transparent_v + { + return _Equal_range(_X); + } + + pair equal_range(const key_type& _X) const { + return _Equal_range(_X); + } + + template + pair equal_range(const _K& _X) const + requires _Is_transparent_v + { + return _Equal_range(_X); + } + + friend bool operator==(const _Derived& _X, const _Derived& _Y) { + return _RANGES equal(_X._Data.keys, _Y._Data.keys) && _RANGES equal(_X._Data.values, _Y._Data.values); + } + + friend auto operator<=>(const _Derived& _X, const _Derived& _Y) { + return _STD lexicographical_compare_three_way( + _X.cbegin(), _X.cend(), _Y.cbegin(), _Y.cend(), _Synth_three_way{}); + } + + friend void swap(_Derived& _X, _Derived& _Y) noexcept { + _X.swap(_Y); + } + + +private: + key_compare _Key_compare; + containers _Data; + + template + requires (same_as, key_type> && same_as, key_type>) + || (constructible_from && constructible_from + && _Is_transparent_v) + bool _Key_equal(_K1&& _X, _K2&& _Y) const { + return !_Key_compare(_STD forward<_K1>(_X), _STD forward<_K2>(_Y)) + && !_Key_compare(_STD forward<_K2>(_Y), _STD forward<_K1>(_X)); + } + + void _Sort() { + _Clear_flat_map_scope_guard _Guard{this}; + auto _Zip_view = _RANGES views::zip(_Data.keys, _Data.values); + _RANGES sort(_Zip_view, value_compare(_Key_compare)); + _Guard._Clearable = nullptr; + } + + void _Dedup() { + _Clear_flat_map_scope_guard _Guard{this}; + auto _Zip_view = _RANGES views::zip(_Data.keys, _Data.values); + auto _Subrange = _RANGES unique( + _Zip_view, [this](const_reference _X, const_reference _Y) { return this->_Key_equal(_X.first, _Y.first); }); + auto _Remaining_count = _STD distance(_Zip_view.begin(), _Subrange.begin()); + _Data.keys.erase(_Data.keys.begin() + _Remaining_count, _Data.keys.end()); + _Data.values.erase(_Data.values.begin() + _Remaining_count, _Data.values.end()); + _Guard._Clearable = nullptr; + } + + void _Insert_exact(const_iterator _Position, key_type&& _Key_val, mapped_type&& _Mapped) { + _Clear_flat_map_scope_guard _Guard{this}; + _Data.keys.insert(_Position._Key_it, _STD move(_Key_val)); + _Data.values.insert(_Position._Mapped_it, _STD move(_Mapped)); + _Guard._Clearable = nullptr; + } + + template + iterator _Emplace_hint(const_iterator _Position, _K&& _Key_val, _Mapped_args_t&&... _Args) + requires is_constructible_v + && (same_as, key_type> + || (constructible_from && _Is_transparent_v) ) + { + static_assert(!(_Multi && _Overwrite_if_exists), + "Overwriting is not supported when the container allows multiple copies of a key."); + const const_iterator _Begin = cbegin(); + const const_iterator _End = cend(); + + bool _Insert_before_position = false; + bool _Insert_after_position_minus_1 = false; + if constexpr (_Multi) { + _Insert_before_position = (_Position == _End) || !_Key_compare(*(_Position._Key_it), _Key_val); + _Insert_after_position_minus_1 = (_Position == _Begin) || !_Key_compare(_Key_val, *(_Position._Key_it - 1)); + } else { + _Insert_before_position = (_Position == _End) || _Key_compare(_Key_val, *(_Position._Key_it)); + _Insert_after_position_minus_1 = (_Position == _Begin) || _Key_compare(*(_Position._Key_it - 1), _Key_val); + } + bool _Hint_is_accurate = _Insert_before_position && _Insert_after_position_minus_1; + + if (_Hint_is_accurate) { + auto _Dist = _STD distance(_Begin._Key_it, _Position._Key_it); + _Insert_exact( + _Position, key_type{_STD forward<_K>(_Key_val)}, mapped_type{_STD forward<_Mapped_args_t>(_Args)...}); + return begin() + _Dist; + } else { + if constexpr (_Overwrite_if_exists) { + if (_Key_equal(_Key_val, *(_Position._Key_it))) { + auto _Dist = _STD distance(_Begin._Key_it, _Position._Key_it); + auto _It = begin() + _Dist; + *(_It._Mapped_it) = mapped_type{_STD forward<_Mapped_args_t>(_Args)...}; + return _It; + } + } + + _Position = lower_bound(_Key_val); + if (_Overwrite_if_exists && _Position != _End && _Key_equal(_Key_val, *(_Position._Key_it))) { + auto _Dist = _STD distance(_Begin._Key_it, _Position._Key_it); + auto _It = begin() + _Dist; + *(_It._Mapped_it) = mapped_type{_STD forward<_Mapped_args_t>(_Args)...}; + return _It; + } else { + auto _Dist = _STD distance(_Begin._Key_it, _Position._Key_it); + _Insert_exact(_Position, key_type{_STD forward<_K>(_Key_val)}, + mapped_type{_STD forward<_Mapped_args_t>(_Args)...}); + return begin() + _Dist; + } + } + } + + template + requires _Is_iterator_v<_InputIterator> + void _Insert_range(_InputIterator _First, _InputIterator _Last) { + _Clear_flat_map_scope_guard _Guard{this}; + + // Insert the new elements at the end + size_type _OldSize = size(); + size_type _NewSize = _STD distance(_First, _Last); + + _Data.keys.reserve(_Data.keys.size() + _NewSize); + _Data.values.reserve(_Data.values.size() + _NewSize); + + for (; _First != _Last; ++_First) { + _Data.keys.emplace_back(_STD move(_First->first)); + _Data.values.emplace_back(_STD move(_First->second)); + } + + // Sort the newly inserted elements + auto _Zip_view = _RANGES views::zip(_Data.keys, _Data.values); + if constexpr (_NeedSorting) { + auto _Zip_view_new_elements = _Zip_view | _RANGES views::drop(_OldSize); + _RANGES sort(_Zip_view_new_elements, value_compare(_Key_compare)); + } + + // Merge the newly inserted elements with the existing elements + _RANGES inplace_merge(_Zip_view, _Zip_view.begin() + _OldSize, value_compare(_Key_compare)); + + if constexpr (_NeedDeduping) { + _Dedup(); + } + + _Guard._Clearable = nullptr; + } + + template + _NODISCARD mapped_type& _At(const _K& _Key_val) + requires same_as<_K, key_type> || _Is_transparent_v + { + iterator _Position = find(_Key_val); + if (_Position == end()) { + _Xout_of_range("std::flat_map::at: the specified key does not exist."); + } else { + return _Position->second; + } + } + + template + iterator _Find(const _K& _X) + requires same_as<_K, key_type> || _Is_transparent_v + { + iterator _Position = lower_bound(_X); + if (_Position != end() && _Key_equal(_Position->first, _X)) { + return _Position; + } else { + return end(); + } + } + + template + const_iterator _Find(const _K& _X) const + requires same_as<_K, key_type> || _Is_transparent_v + { + const_iterator _Position = lower_bound(_X); + if (_Position != cend() && _Key_equal(_Position->first, _X)) { + return _Position; + } else { + return cend(); + } + } + + template + size_type _Count(const _K& _X) const + requires same_as<_K, key_type> || _Is_transparent_v + { + return upper_bound(_X) - lower_bound(_X); + } + + template + bool _Contains(const _K& _X) const + requires same_as<_K, key_type> || _Is_transparent_v + { + return find(_X) != cend(); + } + + template + iterator _Lower_bound(const _K& _X) + requires same_as<_K, key_type> || _Is_transparent_v + { + typename key_container_type::const_iterator _Key_it = + _STD lower_bound(_Data.keys.cbegin(), _Data.keys.cend(), _X, _Key_compare); + auto _Dist = _STD distance(_Data.keys.cbegin(), _Key_it); + typename mapped_container_type::iterator _Val_it = _Data.values.begin() + _Dist; + return iterator{_STD move(_Key_it), _STD move(_Val_it)}; + } + + template + const_iterator _Lower_bound(const _K& _X) const + requires same_as<_K, key_type> || _Is_transparent_v + { + typename key_container_type::const_iterator _Key_it = + _STD lower_bound(_Data.keys.cbegin(), _Data.keys.cend(), _X, _Key_compare); + auto _Dist = _STD distance(_Data.keys.cbegin(), _Key_it); + typename mapped_container_type::const_iterator _Val_it = _Data.values.cbegin() + _Dist; + return const_iterator{_STD move(_Key_it), _STD move(_Val_it)}; + } + + template + iterator _Upper_bound(const _K& _X) + requires same_as<_K, key_type> || _Is_transparent_v + { + typename key_container_type::const_iterator _Key_it = + _STD upper_bound(_Data.keys.cbegin(), _Data.keys.cend(), _X, _Key_compare); + auto _Dist = _STD distance(_Data.keys.cbegin(), _Key_it); + typename mapped_container_type::iterator _Val_it = _Data.values.begin() + _Dist; + return iterator{_STD move(_Key_it), _STD move(_Val_it)}; + } + + template + const_iterator _Upper_bound(const _K& _X) const + requires same_as<_K, key_type> || _Is_transparent_v + { + typename key_container_type::const_iterator _Key_it = + _STD upper_bound(_Data.keys.cbegin(), _Data.keys.cend(), _X, _Key_compare); + auto _Dist = _STD distance(_Data.keys.cbegin(), _Key_it); + typename mapped_container_type::const_iterator _Val_it = _Data.values.cbegin() + _Dist; + return const_iterator{_STD move(_Key_it), _STD move(_Val_it)}; + } + + template + pair _Equal_range(const _K& _X) + requires same_as<_K, key_type> || _Is_transparent_v + { + return {lower_bound(_X), upper_bound(_X)}; + } + + template + pair _Equal_range(const _K& _X) const + requires same_as<_K, key_type> || _Is_transparent_v + { + return {lower_bound(_X), upper_bound(_X)}; + } +}; + +template +class flat_map : public _Flat_Map_Base<_Key, _Mapped, _Compare, _KeyContainer, _MappedContainer, false, + flat_map<_Key, _Mapped, _Compare, _KeyContainer, _MappedContainer>> { +private: + using _MyBase = _Flat_Map_Base<_Key, _Mapped, _Compare, _KeyContainer, _MappedContainer, false, + flat_map<_Key, _Mapped, _Compare, _KeyContainer, _MappedContainer>>; + +public: + using _MyBase::_MyBase; + using _MyBase::operator=; +}; + +template +class flat_multimap : public _Flat_Map_Base<_Key, _Mapped, _Compare, _KeyContainer, _MappedContainer, true, + flat_multimap<_Key, _Mapped, _Compare, _KeyContainer, _MappedContainer>> { +private: + using _MyBase = _Flat_Map_Base<_Key, _Mapped, _Compare, _KeyContainer, _MappedContainer, true, + flat_multimap<_Key, _Mapped, _Compare, _KeyContainer, _MappedContainer>>; + +public: + using _MyBase::_MyBase; + using _MyBase::operator=; +}; + +template _Compare = less> +flat_map(_KeyContainer, _MappedContainer, _Compare = _Compare()) -> flat_map; + +template _Allocator> +flat_map(_KeyContainer, _MappedContainer, _Allocator) -> flat_map, _KeyContainer, _MappedContainer>; + +template _Compare, + _Valid_Allocator_for_flat_map<_KeyContainer, _MappedContainer> _Allocator> +flat_map(_KeyContainer, _MappedContainer, _Compare, _Allocator) -> flat_map; + +template _Compare = less> +flat_map(sorted_unique_t, _KeyContainer, _MappedContainer, _Compare = _Compare()) + -> flat_map; + +template _Allocator> +flat_map(sorted_unique_t, _KeyContainer, _MappedContainer, _Allocator) -> flat_map, _KeyContainer, _MappedContainer>; + +template _Compare, + _Valid_Allocator_for_flat_map<_KeyContainer, _MappedContainer> _Allocator> +flat_map(sorted_unique_t, _KeyContainer, _MappedContainer, _Compare, _Allocator) + -> flat_map; + +template ::first_type>> +flat_map(_InputIterator, _InputIterator, _Compare = _Compare()) + -> flat_map::first_type, typename iter_value_t<_InputIterator>::second_type, + _Compare>; + +template ::first_type>> +flat_map(sorted_unique_t, _InputIterator, _InputIterator, _Compare = _Compare()) + -> flat_map::first_type, typename iter_value_t<_InputIterator>::second_type, + _Compare>; + +template <_RANGES input_range _R, class _Compare = less::first_type>, + class _Allocator = allocator> +flat_map(from_range_t, _R&&, _Compare = _Compare(), _Allocator = _Allocator()) + -> flat_map::first_type, typename _RANGES range_value_t<_R>::second_type, + _Compare, + vector::first_type, + _Rebind_alloc_t<_Allocator, typename _RANGES range_value_t<_R>::first_type>>, + vector::second_type, + _Rebind_alloc_t<_Allocator, typename _RANGES range_value_t<_R>::second_type>>>; + +template <_RANGES input_range _R, class _Allocator> +flat_map(from_range_t, _R&&, _Allocator) -> flat_map::first_type, + typename _RANGES range_value_t<_R>::second_type, less::first_type>, + vector::first_type, + _Rebind_alloc_t<_Allocator, typename _RANGES range_value_t<_R>::first_type>>, + vector::second_type, + _Rebind_alloc_t<_Allocator, typename _RANGES range_value_t<_R>::second_type>>>; + +template > +flat_map(initializer_list>, _Compare = _Compare()) -> flat_map<_Key, _T, _Compare>; + +template > +flat_map(sorted_unique_t, initializer_list>, _Compare = _Compare()) -> flat_map<_Key, _T, _Compare>; + +// Specialization of uses_allocator +template +struct uses_allocator, _Allocator> + : bool_constant && uses_allocator_v<_MappedContainer, _Allocator>> {}; + +template +struct uses_allocator, _Allocator> + : bool_constant && uses_allocator_v<_MappedContainer, _Allocator>> {}; + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // ^^^ supported language mode ^^^ +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _FLAT_MAP_ diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index 74d6d4699e5..9d7610eaed0 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -64,6 +64,7 @@ "execution", "expected", "filesystem", + "flat_map", "format", "forward_list", "fstream", diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 922a71948d8..63bfcc46785 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -324,6 +324,7 @@ // P0288R9 move_only_function // P0323R12 // P0401R6 Providing Size Feedback In The Allocator Interface +// P0429R9 // P0448R4 // P0627R6 unreachable() // P0798R8 Monadic Operations For optional @@ -1820,6 +1821,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #ifdef __cpp_lib_concepts #define __cpp_lib_containers_ranges 202202L #define __cpp_lib_expected 202211L +#define __cpp_lib_flat_map 202207L #define __cpp_lib_formatters 202302L #endif // defined(__cpp_lib_concepts) diff --git a/stl/modules/std.ixx b/stl/modules/std.ixx index 7a5fdbf7cef..36bd4afbdbf 100644 --- a/stl/modules/std.ixx +++ b/stl/modules/std.ixx @@ -61,6 +61,7 @@ export module std; #include #endif // _HAS_CXX23 #include +#include #include #include #include diff --git a/tests/std/include/test_header_units_and_modules.hpp b/tests/std/include/test_header_units_and_modules.hpp index ae0e81eda4e..da06e630897 100644 --- a/tests/std/include/test_header_units_and_modules.hpp +++ b/tests/std/include/test_header_units_and_modules.hpp @@ -236,6 +236,15 @@ void test_filesystem() { assert(info.capacity != static_cast(-1)); } +#if TEST_STANDARD >= 23 +void test_flat_map() { + using namespace std; + puts("Testing ."); + + // FIXME! ADD TEST COVERAGE HERE! +} +#endif // TEST_STANDARD >= 23 + void test_format() { using namespace std; puts("Testing ."); @@ -1140,6 +1149,9 @@ void all_cpp_header_tests() { test_expected(); #endif // TEST_STANDARD >= 23 test_filesystem(); +#if TEST_STANDARD >= 23 + test_flat_map(); +#endif // TEST_STANDARD >= 23 test_format(); test_forward_list(); test_fstream(); diff --git a/tests/std/test.lst b/tests/std/test.lst index 1cb0e33f917..e4d6b362292 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -331,6 +331,7 @@ tests\P0408R7_efficient_access_to_stringbuf_buffer tests\P0414R2_shared_ptr_for_arrays tests\P0415R1_constexpr_complex tests\P0426R1_constexpr_char_traits +tests\P0429R9_flat_map tests\P0433R2_deduction_guides tests\P0448R4_iosfwd tests\P0448R4_spanstream diff --git a/tests/std/tests/P0429R9_flat_map/env.lst b/tests/std/tests/P0429R9_flat_map/env.lst new file mode 100644 index 00000000000..8ac7033b206 --- /dev/null +++ b/tests/std/tests/P0429R9_flat_map/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P0429R9_flat_map/test.cpp b/tests/std/tests/P0429R9_flat_map/test.cpp new file mode 100644 index 00000000000..d3d10569eab --- /dev/null +++ b/tests/std/tests/P0429R9_flat_map/test.cpp @@ -0,0 +1,281 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +concept IsFlatMap = _Is_specialization_v, flat_map>; + +template +bool check_container_requirements(T&&) { + return true; +} + +template +consteval bool check_reversible_container_requirements() + requires _Is_specialization_v, flat_map> +{ + using map_t = remove_cvref_t; + bool result = true; + result &= is_same_v, typename map_t::reverse_iterator>; + result &= is_same_v, typename map_t::const_reverse_iterator>; + result &= is_same_v().begin()), typename map_t::iterator>; + result &= is_same_v().end()), typename map_t::iterator>; + result &= is_same_v().cbegin()), typename map_t::const_iterator>; + result &= is_same_v().cend()), typename map_t::const_iterator>; + result &= is_same_v().rbegin()), typename map_t::reverse_iterator>; + result &= is_same_v().rend()), typename map_t::reverse_iterator>; + result &= is_same_v().crbegin()), typename map_t::const_reverse_iterator>; + result &= is_same_v().crend()), typename map_t::const_reverse_iterator>; + result &= is_convertible_v; + result &= is_convertible_v; + return result; +} + +template +constexpr bool check_reversible_container_requirements(T&&) + requires _Is_specialization_v, flat_map> +{ + return check_reversible_container_requirements(); +} + +template +bool check_requirements(T&& obj) { + return check_container_requirements(forward(obj)) && check_reversible_container_requirements(forward(obj)); +} + +template +bool check_key_content(const T& obj, const typename T::key_container_type& expected) { + const auto& actual = obj.keys(); + if (actual.size() != expected.size()) { + return false; + } + return std::ranges::equal(actual, expected); +} + +template +bool check_value_content(const T& obj, const typename T::mapped_container_type& expected) { + const auto& actual = obj.values(); + if (actual.size() != expected.size()) { + return false; + } + return std::ranges::equal(actual, expected); +} + +template +class MyAllocator : public std::allocator { +public: + using value_type = T; + using std::allocator::allocator; + + static size_t getActiveAllocationCount() { + return s_allocations.load(); + } + + T* allocate(size_t n) { + ++s_allocations; + return std::allocator::allocate(n); + } + + T* allocate_at_least(size_t n) { + ++s_allocations; + return std::allocator::allocate_at_least(n); + } + + void deallocate(T* p, size_t n) noexcept { + --s_allocations; + std::allocator::deallocate(p, n); + } + +private: + static std::atomic s_allocations; +}; + +template +class Packaged { +private: + T value; + +public: + Packaged() : value() {} + template + requires std::constructible_from + Packaged(U&& u) : value(std::forward(u)) {} + + T get() const { + return value; + } + + void set(T t) { + value = t; + } + + friend bool operator==(const Packaged& lhs, const Packaged& rhs) { + return lhs.value == rhs.value; + } + + friend bool operator==(const Packaged& lhs, const T& rhs) { + return lhs.value == rhs; + } + + friend bool operator==(const T& lhs, const Packaged& rhs) { + return lhs == rhs.value; + } + + friend auto operator<=>(const Packaged& lhs, const Packaged& rhs) { + return lhs.value <=> rhs.value; + } +}; + +template +class PackagedCompare : public std::less> { +public: + using std::less>::less; +}; + +template +class TransparentPackagedCompare : public PackagedCompare { +public: + using PackagedCompare::PackagedCompare; + using is_transparent = void; + + bool operator()(const Packaged& lhs, const Packaged& rhs) const { + return PackagedCompare::operator()(lhs, rhs); + } + + bool operator()(const T& lhs, const Packaged& rhs) const { + return lhs < rhs.get(); + } + + bool operator()(const Packaged& lhs, const T& rhs) const { + return lhs.get() < rhs; + } +}; + +template +std::atomic MyAllocator::s_allocations = 0; + +void test_construction() { + { + std::flat_map map; + assert(check_requirements(map)); + assert(check_key_content(map, {})); + assert(check_value_content(map, {})); + } + { + std::vector keys = {0, 1, 2, 3, 4, 2}; + std::vector vals = {44, 2324, 635462, 433, 5, 7}; + std::flat_map map(keys, vals); + assert(check_requirements(map)); + assert(check_key_content(map, {0, 1, 2, 3, 4})); + assert(check_value_content(map, {44, 2324, 635462, 433, 5})); + } + { + std::vector> keys = {0, 1, 2, 3, 4, 2}; + std::vector> vals = {44, 2324, 635462, 433, 5, 7}; + size_t activeAllocations = MyAllocator::getActiveAllocationCount(); + std::flat_map map(keys, vals, MyAllocator()); + assert(check_key_content(map, {0, 1, 2, 3, 4})); + assert(check_value_content(map, {44, 2324, 635462, 433, 5})); + assert(MyAllocator::getActiveAllocationCount() > activeAllocations); + } + { + std::vector keys = {0, 1, 2, 3, 38, 242}; + std::vector vals = {44, 2324, 635462, 433, 5, 7}; + std::flat_map map(std::sorted_unique, keys, vals); + assert(check_requirements(map)); + assert(check_key_content(map, {0, 1, 2, 3, 38, 242})); + assert(check_value_content(map, {44, 2324, 635462, 433, 5, 7})); + } + { + PackagedCompare comp; + std::flat_map, int, PackagedCompare> map(comp); + assert(check_requirements(map)); + assert(check_key_content(map, {})); + assert(check_value_content(map, {})); + } + { + PackagedCompare comp; + MyAllocator> alloc; + std::flat_map, Packaged, PackagedCompare, + std::vector, MyAllocator>>, + std::vector, MyAllocator>>> + map(comp, alloc); + assert(check_requirements(map)); + assert(check_key_content(map, {})); + assert(check_value_content(map, {})); + } + { + MyAllocator> alloc; + std::flat_map, Packaged, PackagedCompare, + std::vector, MyAllocator>>, + std::vector, MyAllocator>>> + map(alloc); + assert(check_requirements(map)); + assert(check_key_content(map, {})); + assert(check_value_content(map, {})); + } + { + MyAllocator> alloc; + std::flat_map, int, PackagedCompare, std::vector, MyAllocator>>, + std::vector>> + map(alloc); + assert(check_requirements(map)); + assert(check_key_content(map, {})); + assert(check_value_content(map, {})); + } + { + PackagedCompare comp; + MyAllocator alloc; + std::vector, MyAllocator>> keys = {0, 1, 2, 3, 4, 2}; + std::vector> vals = {44, 2324, 635462, 433, 5, 7}; + std::flat_map map(keys, vals, comp, alloc); + assert(check_requirements(map)); + assert(check_key_content(map, {0, 1, 2, 3, 4})); + assert(check_value_content(map, {44, 2324, 635462, 433, 5})); + } + { + TransparentPackagedCompare comp; + MyAllocator alloc; + std::vector, MyAllocator>> keys = {0, 1, 2, 3, 4, 2}; + std::vector> vals = {44, 2324, 635462, 433, 5, 7}; + std::flat_map map(keys, vals, comp, alloc); + assert(check_requirements(map)); + assert(check_key_content(map, {0, 1, 2, 3, 4})); + assert(check_value_content(map, {44, 2324, 635462, 433, 5})); + } +} + + +struct Incomplete; +template +struct MyType { + T* ptr; + + bool operator==(const MyType&) const = default; + auto operator<=>(const MyType&) const = default; +}; + +void test_pointer_to_incomplete_type() { + struct Test { + std::unique_ptr> ptr; + }; + + Test t; + std::flat_map, std::shared_ptr>> map; +} + +int main() { + test_construction(); + test_pointer_to_incomplete_type(); + return 0; +}