From d8ef55d62c0ef3955bb886470800f964e01f2845 Mon Sep 17 00:00:00 2001 From: Stefan <29021710+Saalvage@users.noreply.github.com> Date: Thu, 22 Jun 2023 18:01:18 +0200 Subject: [PATCH 001/134] Implement `` (#3817) Co-authored-by: A. Jiang <23228989+frederick-vs-ja@users.noreply.github.com> Co-authored-by: Stephan T. Lavavej Co-authored-by: Alex Guteniev <3995422+alexguteniev@users.noreply.github.com> --- stl/CMakeLists.txt | 1 + stl/inc/__msvc_all_public_headers.hpp | 1 + stl/inc/flat_set | 721 ++++++++++++++++++ stl/inc/header-units.json | 1 + stl/inc/yvals_core.h | 2 + stl/modules/std.ixx | 1 + .../include/test_header_units_and_modules.hpp | 8 + tests/std/test.lst | 1 + tests/std/tests/P1222R4_flat_set/env.lst | 4 + tests/std/tests/P1222R4_flat_set/test.cpp | 212 +++++ .../importable_cxx_library_headers.jsonc | 1 + .../test.cpp | 1 + .../test.compile.pass.cpp | 14 + .../include_each_header_alone_matrix.lst | 1 + 14 files changed, 969 insertions(+) create mode 100644 stl/inc/flat_set create mode 100644 tests/std/tests/P1222R4_flat_set/env.lst create mode 100644 tests/std/tests/P1222R4_flat_set/test.cpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index b559f9c8336..1befa5febb8 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -156,6 +156,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_set ${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 ccb3267943a..248d8819dc2 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_set b/stl/inc/flat_set new file mode 100644 index 00000000000..67dc93c4da7 --- /dev/null +++ b/stl/inc/flat_set @@ -0,0 +1,721 @@ +// flat_set standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _FLAT_SET_ +#define _FLAT_SET_ +#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 + +_STD_BEGIN + +template +struct _NODISCARD _Clear_scope_guard { + _Ty* _Clearable; + ~_Clear_scope_guard() { + if (_Clearable) { + _Clearable->clear(); + } + } +}; + +template +concept _Allocator_for = uses_allocator_v<_Container, _Alloc>; + +template +class _Base_flat_set { +private: + static constexpr bool _Keylt_transparent = _Is_transparent_v<_Keylt>; + +public: + static_assert(same_as<_Kty, typename _Container::value_type>, + "The C++ Standard dictates that the Key type must be the " + "same as the container's value type [flatset.overview]"); + + using key_type = _Kty; + using value_type = _Kty; + using key_compare = _Keylt; + using value_compare = _Keylt; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = _Container::size_type; + using difference_type = _Container::difference_type; + using iterator = _Container::iterator; + using const_iterator = _Container::const_iterator; + using reverse_iterator = _STD reverse_iterator; + using const_reverse_iterator = _STD reverse_iterator; + using container_type = _Container; + + static_assert(random_access_iterator, "The C++ Standard forbids containers without random " + "access iterators from being adapted. See [flatset.overview]."); + + _Base_flat_set() : _My_pair(_Zero_then_variadic_args_t{}, _Keylt()) {} + + explicit _Base_flat_set(container_type _Cont, const key_compare& _Comp = key_compare()) + : _My_pair(_One_then_variadic_args_t{}, _STD move(_Cont), _Comp) { + _Make_invariants_fulfilled(); + } + template <_Allocator_for _Alloc> + _Base_flat_set(const container_type& _Cont, const _Alloc& _Al) : _Base_flat_set(container_type(_Cont, _Al)) {} + template <_Allocator_for _Alloc> + _Base_flat_set(const container_type& _Cont, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(container_type(_Cont, _Al), _Comp) {} + + _Base_flat_set(_Tsorted, container_type _Cont, const key_compare& _Comp = key_compare()) + : _My_pair(_One_then_variadic_args_t{}, _STD move(_Cont), _Comp) { + _Assert_after_sorted_input(); + } + template <_Allocator_for _Alloc> + _Base_flat_set(_Tsorted _Tsort, const container_type& _Cont, const _Alloc& _Al) + : _Base_flat_set(_Tsort, container_type(_Cont, _Al)) {} + template <_Allocator_for _Alloc> + _Base_flat_set(_Tsorted _Tsort, const container_type& _Cont, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(_Tsort, container_type(_Cont, _Al), _Comp) {} + + explicit _Base_flat_set(const key_compare& _Comp) : _My_pair(_Zero_then_variadic_args_t{}, _Comp) {} + template <_Allocator_for _Alloc> + _Base_flat_set(const key_compare& _Comp, const _Alloc& _Al) : _Base_flat_set(_Comp, container_type(_Al)) {} + template <_Allocator_for _Alloc> + explicit _Base_flat_set(const _Alloc& _Al) : _Base_flat_set(container_type(_Al)) {} + + template + _Base_flat_set(_Iter _First, _Iter _Last, const key_compare& _Comp = key_compare()) + : _Base_flat_set(container_type(_First, _Last), _Comp) {} + template _Alloc> + _Base_flat_set(_Iter _First, _Iter _Last, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(container_type(_First, _Last, _Al), _Comp) {} + template _Alloc> + _Base_flat_set(_Iter _First, _Iter _Last, const _Alloc& _Al) : _Base_flat_set(container_type(_First, _Last, _Al)) {} + + template <_Container_compatible_range<_Kty> _Rng> + _Base_flat_set(from_range_t, _Rng&& _Range, const key_compare& _Comp = key_compare()) + : _Base_flat_set(container_type(from_range, _STD forward<_Rng>(_Range)), _Comp) {} + template <_Container_compatible_range<_Kty> _Rng, _Allocator_for _Alloc> + _Base_flat_set(from_range_t, _Rng&& _Range, const _Alloc& _Al) + : _Base_flat_set(container_type(from_range, _STD forward<_Rng>(_Range), _Al)) {} + template <_Container_compatible_range<_Kty> _Rng, _Allocator_for _Alloc> + _Base_flat_set(from_range_t, _Rng&& _Range, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(container_type(from_range, _STD forward<_Rng>(_Range), _Al), _Comp) {} + + template + _Base_flat_set(_Tsorted _Tsort, _Iter _First, _Iter _Last, const key_compare& _Comp = key_compare()) + : _Base_flat_set(_Tsort, container_type(_First, _Last), _Comp) {} + template _Alloc> + _Base_flat_set(_Tsorted _Tsort, _Iter _First, _Iter _Last, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(_Tsort, container_type(_First, _Last, _Al), _Comp) {} + template _Alloc> + _Base_flat_set(_Tsorted _Tsort, _Iter _First, _Iter _Last, const _Alloc& _Al) + : _Base_flat_set(_Tsort, container_type(_First, _Last, _Al)) {} + + _Base_flat_set(initializer_list<_Kty> _Ilist, const key_compare& _Comp = key_compare()) + : _Base_flat_set(_Ilist.begin(), _Ilist.end(), _Comp) {} + template <_Allocator_for _Alloc> + _Base_flat_set(initializer_list<_Kty> _Ilist, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(container_type(_Ilist.begin(), _Ilist.end(), _Al), _Comp) {} + template <_Allocator_for _Alloc> + _Base_flat_set(initializer_list<_Kty> _Ilist, const _Alloc& _Al) + : _Base_flat_set(container_type(_Ilist.begin(), _Ilist.end(), _Al)) {} + + _Base_flat_set(_Tsorted _Tsort, initializer_list<_Kty> _Ilist, const key_compare& _Comp = key_compare()) + : _Base_flat_set(_Tsort, _Ilist.begin(), _Ilist.end(), _Comp) {} + template <_Allocator_for _Alloc> + _Base_flat_set(_Tsorted _Tsort, initializer_list<_Kty> _Ilist, const key_compare& _Comp, const _Alloc& _Al) + : _Base_flat_set(_Tsort, container_type(_Ilist.begin(), _Ilist.end(), _Al), _Comp) {} + template <_Allocator_for _Alloc> + _Base_flat_set(_Tsorted _Tsort, initializer_list<_Kty> _Ilist, const _Alloc& _Al) + : _Base_flat_set(_Tsort, container_type(_Ilist.begin(), _Ilist.end(), _Al)) {} + + _Deriv& operator=(initializer_list<_Kty> _Ilist) { + _Get_cont() = container_type(_Ilist.begin(), _Ilist.end()); + _Make_invariants_fulfilled(); + return static_cast<_Deriv&>(*this); + } + + _NODISCARD iterator begin() noexcept { + return _Get_cont().begin(); + } + _NODISCARD const_iterator begin() const noexcept { + return _Get_cont().begin(); + } + _NODISCARD iterator end() noexcept { + return _Get_cont().end(); + } + _NODISCARD const_iterator end() const noexcept { + return _Get_cont().end(); + } + _NODISCARD reverse_iterator rbegin() noexcept { + return _Get_cont().rbegin(); + } + _NODISCARD const_reverse_iterator rbegin() const noexcept { + return _Get_cont().rbegin(); + } + _NODISCARD reverse_iterator rend() noexcept { + return _Get_cont().rend(); + } + _NODISCARD const_reverse_iterator rend() const noexcept { + return _Get_cont().rend(); + } + _NODISCARD const_iterator cbegin() const noexcept { + return _Get_cont().cbegin(); + } + _NODISCARD const_iterator cend() const noexcept { + return _Get_cont().cend(); + } + _NODISCARD const_reverse_iterator crbegin() const noexcept { + return _Get_cont().crbegin(); + } + _NODISCARD const_reverse_iterator crend() const noexcept { + return _Get_cont().crend(); + } + + _NODISCARD_EMPTY_MEMBER bool empty() const noexcept { + return _Get_cont().empty(); + } + _NODISCARD size_type size() const noexcept { + return _Get_cont().size(); + } + _NODISCARD size_type max_size() const noexcept { + return _Get_cont().max_size(); + } + + template + auto emplace(_Args&&... _Vals) { + insert<_Kty>(_Kty{_STD forward<_Args>(_Vals)...}); + } + + template + iterator emplace_hint(const_iterator _Hint, _Args&&... _Vals) { + return _Emplace_hint(_Hint, _Kty{_STD forward<_Args>(_Vals)...}); + } + + auto insert(const value_type& _Val) { + return _Insert<_Kty>(_Val); + } + auto insert(value_type&& _Val) { + return _Insert(_STD move(_Val)); + } + + template + requires (!_Multi && _Keylt_transparent && is_constructible_v<_Kty, _Other>) + auto insert(_Other&& _Val) { + return _Insert(_STD forward<_Other>(_Val)); + } + + template + requires (!_Multi && _Keylt_transparent && is_constructible_v<_Kty, _Other>) + iterator insert(const_iterator _Hint, _Other&& _Val) { + return _Emplace_hint(_Hint, _STD forward<_Other>(_Val)); + } + + iterator insert(const_iterator _Hint, const value_type& _Val) { + return _Emplace_hint(_Hint, _Val); + } + iterator insert(const_iterator _Hint, value_type&& _Val) { + return _Emplace_hint(_Hint, _STD move(_Val)); + } + + template + void insert(const _Iter& _First, const _Iter& _Last) { + _Insert_range(_First, _Last); + } + + template + void insert(_Tsorted, _Iter _First, _Iter _Last) { + _Insert_range(_First, _Last); + } + + template <_Container_compatible_range<_Kty> _Rng> + void insert_range(_Rng&& _Range) { + const size_type _Old_size = size(); + _Get_cont().append_range(_STD forward<_Rng>(_Range)); + _Restore_invariants_after_insert(_Old_size); + } + + _NODISCARD container_type extract() && { + // The container NEEDS to be cleared no matter what, + // which is not guaranteed by simply moving it away + // ("... valid but unspecified ...") + container_type& _Cont = _Get_cont(); + _Clear_scope_guard _Guard{this}; + container_type _Temp = _STD move(_Cont); + return _Temp; + } + + void replace(container_type&& _Cont) { + _Get_cont() = _STD move(_Cont); + _Assert_after_sorted_input(); + } + + iterator erase(iterator _Where) { + return _Get_cont().erase(_Where); + } + iterator erase(const_iterator _Where) { + return _Get_cont().erase(_Where); + } + size_type erase(const _Kty& _Val) { + return _Erase(_Val); + } + + template + requires _Keylt_transparent + size_type erase(_Other&& _Val) { + return _Erase(_STD forward<_Other>(_Val)); + } + + iterator erase(const_iterator _First, const_iterator _Last) { + return _Get_cont().erase(_First, _Last); + } + + void swap(_Deriv& _Other) noexcept { + _RANGES swap(_Get_comp(), _Other._Get_comp()); + _RANGES swap(_Get_cont(), _Other._Get_cont()); + } + + void clear() noexcept { + _Get_cont().clear(); + } + + _NODISCARD key_compare key_comp() const { + return _Get_comp(); + } + _NODISCARD value_compare value_comp() const { + return _Get_comp(); + } + + _NODISCARD iterator find(const _Kty& _Val) { + return _Find(_Val); + } + + _NODISCARD const_iterator find(const _Kty& _Val) const { + return _Find(_Val); + } + + template + requires _Keylt_transparent + _NODISCARD iterator find(const _Other& _Val) { + return _Find(_Val); + } + + template + requires _Keylt_transparent + _NODISCARD const_iterator find(const _Other& _Val) const { + return _Find(_Val); + } + + _NODISCARD size_type count(const _Kty& _Val) const { + const auto [_First, _Last] = equal_range(_Val); + return _STD distance(_First, _Last); + } + + template + requires _Keylt_transparent + _NODISCARD size_type count(const _Other& _Val) const { + const auto [_First, _Last] = equal_range(_Val); + return _STD distance(_First, _Last); + } + + _NODISCARD bool contains(const _Kty& _Val) const { + return find(_Val) != end(); + } + template + requires _Keylt_transparent + _NODISCARD bool contains(const _Other& _Val) const { + return find(_Val) != end(); + } + _NODISCARD iterator lower_bound(const _Kty& _Val) { + return _STD lower_bound(begin(), end(), _Val, _Get_comp()); + } + _NODISCARD const_iterator lower_bound(const _Kty& _Val) const { + return _STD lower_bound(cbegin(), cend(), _Val, _Get_comp()); + } + + template + requires _Keylt_transparent + _NODISCARD iterator lower_bound(const _Other& _Val) { + return _STD lower_bound(begin(), end(), _Val, _Get_comp()); + } + + template + requires _Keylt_transparent + _NODISCARD const_iterator lower_bound(const _Other& _Val) const { + return _STD lower_bound(cbegin(), cend(), _Val, _Get_comp()); + } + + _NODISCARD iterator upper_bound(const _Kty& _Val) { + return _STD upper_bound(begin(), end(), _Val, _Get_comp()); + } + + _NODISCARD const_iterator upper_bound(const _Kty& _Val) const { + return _STD upper_bound(cbegin(), cend(), _Val, _Get_comp()); + } + + template + requires _Keylt_transparent + _NODISCARD iterator upper_bound(const _Other& _Val) { + return _STD upper_bound(begin(), end(), _Val, _Get_comp()); + } + + template + requires _Keylt_transparent + _NODISCARD const_iterator upper_bound(const _Other& _Val) const { + return _STD upper_bound(cbegin(), cend(), _Val, _Get_comp()); + } + + _NODISCARD pair equal_range(const _Kty& _Val) { + return _STD equal_range(begin(), end(), _Val, _Get_comp()); + } + + _NODISCARD pair equal_range(const _Kty& _Val) const { + return _STD equal_range(cbegin(), cend(), _Val, _Get_comp()); + } + + template + requires _Keylt_transparent + _NODISCARD pair equal_range(const _Other& _Val) { + return _STD equal_range(begin(), end(), _Val, _Get_comp()); + } + + template + requires _Keylt_transparent + _NODISCARD pair equal_range(const _Other& _Val) const { + return _STD equal_range(cbegin(), cend(), _Val, _Get_comp()); + } + + _NODISCARD friend bool operator==(const _Deriv& _Lhs, const _Deriv& _Rhs) { + return _RANGES equal(_Lhs, _Rhs); + } + + _NODISCARD friend _Synth_three_way_result<_Kty> operator<=>(const _Deriv& _Lhs, const _Deriv& _Rhs) { + return _STD lexicographical_compare_three_way( + _Lhs.cbegin(), _Lhs.cend(), _Rhs.cbegin(), _Rhs.cend(), _Synth_three_way{}); + } + + friend void swap(_Deriv& _Lhs, _Deriv& _Rhs) noexcept { + _Lhs.swap(_Rhs); + } + +private: + void inline _Assert_after_sorted_input() const { + _STL_ASSERT(_STD is_sorted(begin(), end(), _Get_comp()), "Input was not sorted!"); + if constexpr (!_Multi) { + _STL_ASSERT(_Is_unique(), "Input was not unique!"); + } + } + + bool _Is_unique() const { + if (empty()) { + return true; + } + const_iterator _End = cend(); + const_iterator _It = begin(); + while (++_It != _End) { + if (_Keys_equal(*(_It - 1), *_It)) { + return false; + } + } + return true; + } + + template + requires (_Keylt_transparent && is_constructible_v<_Kty, _Ty>) || is_same_v<_Ty, _Kty> + void _Emplace_hint(const_iterator _Where, _Ty&& _Val) { + _Container& _Cont = _Get_cont(); + _Keylt& _Compare = _Get_comp(); + const iterator _Begin = begin(); + const iterator _End = end(); + + if (_Where == _End || !_Compare(*_Where, _Val)) { + // _Val <= *_Where + // Left of _Where + if (_Where == _Begin || !_Compare(_Val, *(_Where - 1))) { + // _Val >= (*_Where - 1) + // Insert before _Where + } else { + // _Val < (*_Where - 1) + _Where = _STD upper_bound(_Begin, _Where, _Val, _Compare); + } + } else { + // _Val > *_Where + // Right of _Where + _Where = _STD lower_bound(_Where + 1, _End, _Val, _Compare); + } + + if constexpr (_Multi) { + return _Cont.insert(_Where, _STD forward<_Ty>(_Val)); + } else { + if (_Where == _End || !_Keys_equal(_Val, *_Where)) { + return _Cont.insert(_Where, _STD forward<_Ty>(_Val)); + } + return _Where; + } + } + + template + void _Insert_range(_Iter _First, _Iter _Last) { + const size_type _Old_size = size(); + _Container& _Cont = _Get_cont(); + _Cont.insert(_Cont.end(), _First, _Last); + _Restore_invariants_after_insert<_Presorted>(_Old_size); + } + + template + requires (!_Multi && _Keylt_transparent && is_constructible_v<_Kty, _Ty>) || is_same_v<_Ty, _Kty> + auto _Insert(_Ty&& _Val) { + _Container& _Cont = _Get_cont(); + const iterator _End = end(); + const iterator _Where = lower_bound(_Val); + if constexpr (_Multi) { + return _Cont.emplace(_Where, _STD forward<_Ty>(_Val)); + } else { + if (_Where != _End && _Keys_equal(*_Where, _Val)) { + return pair{_Where, false}; + } + return pair{_Cont.emplace(_Where, _STD forward<_Ty>(_Val)), true}; + } + } + + template + requires _Keylt_transparent || is_same_v<_Ty, _Kty> + size_type _Erase(_Ty&& _Val) { + const auto [_First, _Last] = equal_range(_Val); + + const difference_type _Removed = _STD distance(_First, _Last); + _Get_cont().erase(_First, _Last); + return _Removed; + } + + template + requires _Keylt_transparent || is_same_v<_Other, _Kty> + _NODISCARD iterator _Find(const _Other& _Val) { + const iterator _End = end(); + const iterator _Where = lower_bound(_Val); + if (_Where != _End && _Keys_equal(*_Where, _Val)) { + return _Where; + } else { + return _End; + } + } + + template + requires _Keylt_transparent || is_same_v<_Other, _Kty> + _NODISCARD const_iterator _Find(const _Other& _Val) const { + const iterator _End = end(); + const iterator _Where = lower_bound(_Val); + if (_Where != _End && _Keys_equal(*_Where, _Val)) { + return _Where; + } else { + return _End; + } + } + + template + requires _Keylt_transparent || (is_same_v<_Kty, _Lhty> && is_same_v<_Lhty, _Rhty>) + _NODISCARD bool _Keys_equal(const _Lhty& _Lhs, const _Rhty& _Rhs) const { + const key_compare& _Compare = _Get_comp(); + return !_Compare(_Lhs, _Rhs) && !_Compare(_Rhs, _Lhs); + } + + // O(N) if already sorted. + void _Sort_potentially_sorted(const iterator& _Begin, const iterator& _End) { + key_compare& _Compare = _Get_comp(); + const iterator _Begin_unsorted = _STD is_sorted_until(_Begin, _End, _Compare); + + _STD sort(_Begin_unsorted, _End, _Compare); + + _STD inplace_merge(begin(), _Begin_unsorted, _End, _Compare); + } + + void _Erase_dupes_if_needed() { + if constexpr (!_Multi) { + iterator _End = end(); + iterator _New_end = + _STD unique(begin(), _End, [&](const _Kty& _Lhs, const _Kty& _Rhs) { return _Keys_equal(_Lhs, _Rhs); }); + _Get_cont().erase(_New_end, _End); + } + if constexpr (!_Multi) { + _STL_INTERNAL_CHECK(_Is_unique()); + } + } + + template + void _Restore_invariants_after_insert(const size_type& _Old_size) { + key_compare& _Compare = _Get_comp(); + const iterator _Old_end = begin() + static_cast(_Old_size); + const iterator _New_end = end(); + + if constexpr (!_Presorted) { + _STD sort(_Old_end, _New_end, _Compare); + } else { + _STL_ASSERT(_STD is_sorted(_Old_end, _New_end, _Compare), "Input was not sorted!"); + } + + _STD inplace_merge(begin(), _Old_end, _New_end, _Compare); + + _STL_INTERNAL_CHECK(_STD is_sorted(begin(), end(), _Get_comp())); + + _Erase_dupes_if_needed(); + } + + void _Make_invariants_fulfilled() { + const iterator _Begin = begin(); + const iterator _End = end(); + + if (_Begin == _End) { + return; + } + + _Sort_potentially_sorted(_Begin, _End); + _STL_INTERNAL_CHECK(_STD is_sorted(begin(), end(), _Get_comp())); + + _Erase_dupes_if_needed(); + } + + _NODISCARD const _Container& _Get_cont() const noexcept { + return _My_pair._Get_first(); + } + + _NODISCARD _Container& _Get_cont() noexcept { + return _My_pair._Get_first(); + } + + _NODISCARD const key_compare& _Get_comp() const noexcept { + return _My_pair._Myval2; + } + + _NODISCARD key_compare& _Get_comp() noexcept { + return _My_pair._Myval2; + } + + _Compressed_pair _My_pair; +}; + +_EXPORT_STD struct sorted_unique_t { + explicit sorted_unique_t() = default; +}; +_EXPORT_STD inline constexpr sorted_unique_t sorted_unique{}; + +_EXPORT_STD struct sorted_equivalent_t { + explicit sorted_equivalent_t() = default; +}; +_EXPORT_STD inline constexpr sorted_equivalent_t sorted_equivalent{}; + +_EXPORT_STD template , class _Container = vector<_Kty>> +class flat_set + : public _Base_flat_set<_Kty, _Keylt, _Container, false, flat_set<_Kty, _Keylt, _Container>, sorted_unique_t> { +public: + using _Base_flat_set<_Kty, _Keylt, _Container, false, flat_set, sorted_unique_t>::_Base_flat_set; +}; + +_EXPORT_STD template , class _Container = vector<_Kty>> +class flat_multiset : public _Base_flat_set<_Kty, _Keylt, _Container, true, flat_multiset<_Kty, _Keylt, _Container>, + sorted_equivalent_t> { +public: + using _Base_flat_set<_Kty, _Keylt, _Container, true, flat_multiset, sorted_equivalent_t>::_Base_flat_set; +}; + +_EXPORT_STD template +size_t erase_if(flat_set<_Kty, _Keylt, _Container>& _Val, _Pred _Predicate) { + _Clear_scope_guard _Guard{&_Val}; + return _Erase_remove_if(_Val, _Pass_fn(_Predicate)); +} + +_EXPORT_STD template +size_t erase_if(flat_multiset<_Kty, _Keylt, _Container>& _Val, _Pred _Predicate) { + _Clear_scope_guard _Guard{&_Val}; + return _Erase_remove_if(_Val, _Pass_fn(_Predicate)); +} + +template +struct uses_allocator, _Alloc> + : bool_constant> {}; + +template +struct uses_allocator, _Alloc> + : bool_constant> {}; + +template > +flat_set(_Container, _Keylt = _Keylt()) -> flat_set; +template +flat_set(_Container, _Alloc) + -> flat_set, _Container>; +template +flat_set(_Container, _Keylt, _Alloc) -> flat_set; + +template > +flat_set(sorted_unique_t, _Container, _Keylt = _Keylt()) + -> flat_set; +template +flat_set(sorted_unique_t, _Container, _Alloc) + -> flat_set, _Container>; +template +flat_set(sorted_unique_t, _Container, _Keylt, _Alloc) -> flat_set; + +template >> +flat_set(_Iter, _Iter, _Keylt = _Keylt()) -> flat_set, _Keylt>; +template >> +flat_set(sorted_unique_t, _Iter, _Iter, _Keylt = _Keylt()) -> flat_set, _Keylt>; +template <_RANGES input_range _Range, class _Keylt = less<_RANGES range_value_t<_Range>>, + class _Alloc = allocator<_RANGES range_value_t<_Range>>> +flat_set(from_range_t, _Range&&, _Keylt = _Keylt(), _Alloc = _Alloc()) -> flat_set<_RANGES range_value_t<_Range>, + _Keylt, vector<_RANGES range_value_t<_Range>, _Rebind_alloc_t<_Alloc, _RANGES range_value_t<_Range>>>>; +template <_RANGES input_range _Range, class _Alloc> +flat_set(from_range_t, _Range&&, _Alloc) -> flat_set<_RANGES range_value_t<_Range>, less<_RANGES range_value_t<_Range>>, + vector<_RANGES range_value_t<_Range>, _Rebind_alloc_t<_Alloc, _RANGES range_value_t<_Range>>>>; +template > +flat_set(initializer_list<_Kty>, _Keylt = _Keylt()) -> flat_set<_Kty, _Keylt>; +template > +flat_set(sorted_unique_t, initializer_list<_Kty>, _Keylt = _Keylt()) -> flat_set<_Kty, _Keylt>; + + +template > +flat_multiset(_Container, _Keylt = _Keylt()) -> flat_multiset; +template +flat_multiset(_Container, _Alloc) + -> flat_multiset, _Container>; +template +flat_multiset(_Container, _Keylt, _Alloc) -> flat_multiset; + +template > +flat_multiset(sorted_equivalent_t, _Container, _Keylt = _Keylt()) + -> flat_multiset; +template +flat_multiset(sorted_equivalent_t, _Container, _Alloc) + -> flat_multiset, _Container>; +template +flat_multiset(sorted_equivalent_t, _Container, _Keylt, _Alloc) + -> flat_multiset; + +template >> +flat_multiset(_Iter, _Iter, _Keylt = _Keylt()) -> flat_multiset, iter_value_t<_Iter>, _Keylt>; +template >> +flat_multiset(sorted_equivalent_t, _Iter, _Iter, _Keylt = _Keylt()) + -> flat_multiset, iter_value_t<_Iter>, _Keylt>; +template <_RANGES input_range _Range, class _Keylt = less<_RANGES range_value_t<_Range>>, + class _Alloc = allocator<_RANGES range_value_t<_Range>>> +flat_multiset(from_range_t, _Range&&, _Keylt = _Keylt(), _Alloc = _Alloc()) + -> flat_multiset<_RANGES range_value_t<_Range>, _Keylt, + vector<_RANGES range_value_t<_Range>, _Rebind_alloc_t<_Alloc, _RANGES range_value_t<_Range>>>>; +template <_RANGES input_range _Range, class _Alloc> +flat_multiset(from_range_t, _Range&&, _Alloc) + -> flat_multiset<_RANGES range_value_t<_Range>, less<_RANGES range_value_t<_Range>>, + vector<_RANGES range_value_t<_Range>, _Rebind_alloc_t<_Alloc, _RANGES range_value_t<_Range>>>>; +template > +flat_multiset(initializer_list<_Kty>, _Keylt = _Keylt()) -> flat_multiset<_Kty, _Keylt>; +template > +flat_multiset(sorted_equivalent_t, initializer_list<_Kty>, _Keylt = _Keylt()) -> flat_multiset<_Kty, _Keylt>; + +_STD_END + +#endif // ^^^ supported language mode ^^^ +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _FLAT_SET_ diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index 0966ec2accf..f4e4048b3b7 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -63,6 +63,7 @@ "execution", "expected", "filesystem", + "flat_set", "format", "forward_list", "fstream", diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index ceb2fce5ed8..761d5e2291d 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -316,6 +316,7 @@ // P1132R7 out_ptr(), inout_ptr() // P1147R1 Printing volatile Pointers // P1206R7 Conversions From Ranges To Containers +// P1222R4 // P1223R5 ranges::find_last, ranges::find_last_if, ranges::find_last_if_not // P1272R4 byteswap() // P1328R1 constexpr type_info::operator==() @@ -1722,6 +1723,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_set 202207L #endif // __cpp_lib_concepts #define __cpp_lib_forward_like 202207L diff --git a/stl/modules/std.ixx b/stl/modules/std.ixx index 326d944d74f..cfae4e6fc95 100644 --- a/stl/modules/std.ixx +++ b/stl/modules/std.ixx @@ -59,6 +59,7 @@ export module std; #include #include #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 ce362844dc7..6710a8e7099 100644 --- a/tests/std/include/test_header_units_and_modules.hpp +++ b/tests/std/include/test_header_units_and_modules.hpp @@ -234,6 +234,13 @@ void test_filesystem() { assert(info.capacity != static_cast(-1)); } +void test_flat_set() { + using namespace std; + puts("Testing ."); + + // FIXME! ADD TEST COVERAGE HERE! +} + void test_format() { using namespace std; puts("Testing ."); @@ -1082,6 +1089,7 @@ void all_cpp_header_tests() { test_execution(); test_expected(); test_filesystem(); + test_flat_set(); test_format(); test_forward_list(); test_fstream(); diff --git a/tests/std/test.lst b/tests/std/test.lst index 822cf153b82..0c0c4e860f9 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -542,6 +542,7 @@ tests\P1206R7_vector_assign_range tests\P1206R7_vector_from_range tests\P1206R7_vector_insert_range tests\P1208R6_source_location +tests\P1222R4_flat_set tests\P1223R5_ranges_alg_find_last tests\P1223R5_ranges_alg_find_last_if tests\P1223R5_ranges_alg_find_last_if_not diff --git a/tests/std/tests/P1222R4_flat_set/env.lst b/tests/std/tests/P1222R4_flat_set/env.lst new file mode 100644 index 00000000000..18e2d7c71ec --- /dev/null +++ b/tests/std/tests/P1222R4_flat_set/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P1222R4_flat_set/test.cpp b/tests/std/tests/P1222R4_flat_set/test.cpp new file mode 100644 index 00000000000..6a3ae2e1223 --- /dev/null +++ b/tests/std/tests/P1222R4_flat_set/test.cpp @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace ranges; + +template +void assert_container_requirements(const T& s) { + T m = s; + assert(m == s); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_convertible_v); + static_assert(is_same_v m.end()), strong_ordering>); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + T my_moved = std::move(m); + assert(!(my_moved != s)); + + T empty{}; + assert(empty.empty()); + + T non_empty = s; + empty.swap(non_empty); + assert(non_empty.empty()); + assert(empty == s); + + std::swap(empty, non_empty); + assert(empty.empty()); + assert(non_empty == s); + + assert(s.cbegin() <= s.cend()); + assert(s.cbegin() < s.cend() || s.empty()); + + assert(m.begin() <= m.end()); + assert(m.begin() < m.end() || m.empty()); + + assert(static_cast(s.cend() - s.cbegin()) == s.size()); +} + +template +void assert_reversible_container_requirements(const T& s) { + static_assert(is_same_v, typename T::reverse_iterator>); + static_assert(is_same_v, typename T::const_reverse_iterator>); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_convertible_v); +} + +template +void assert_all_requirements_and_equals(const T& s, const initializer_list& il) { + assert_container_requirements(s); + assert_reversible_container_requirements(s); + + auto val_comp = s.value_comp(); + auto begin_it = s.cbegin(); + auto end_it = s.cend(); + assert(std::is_sorted(begin_it, end_it, val_comp)); + if constexpr (!_Is_specialization_v) { + if (!s.empty()) { + auto it = begin_it; + while (++it != end_it) { + assert(val_comp(*(it - 1), *it)); + } + } + } + + if (s.size() != il.size() || !std::equal(s.begin(), s.end(), il.begin())) { + cout << "Expected: {"; + for (auto&& e : il) { + cout << e << ", "; + } + cout << "}" << endl; + cout << "Got: {"; + for (auto&& e : s) { + cout << e << ", "; + } + cout << "}" << endl; + assert(false); + } +} + +template +void assert_basic() { + T s{3, 2, 2, 2, 1}; + assert_all_requirements_and_equals(s, {1, 2, 3}); + + s.insert(43); + assert_all_requirements_and_equals(s, {1, 2, 3, 43}); + + int my_ints[] = {1, 2, 3, 4, 55}; + s.insert_range(my_ints); + assert_all_requirements_and_equals(s, {1, 2, 3, 4, 43, 55}); +} + +template +void test_constructors() { + using lt = std::less; + using gt = std::greater; + + assert_all_requirements_and_equals(flat_set(), {}); + assert_all_requirements_and_equals(flat_multiset(), {}); + assert_all_requirements_and_equals(flat_set(C{3, 7, 1, 85, 222, 1}), {1, 3, 7, 85, 222}); + assert_all_requirements_and_equals(flat_multiset(C{3, 7, 1, 85, 7, 222, 1}), {1, 1, 3, 7, 7, 85, 222}); + assert_all_requirements_and_equals(flat_set(C{1, 2, 3, 3}, gt()), {3, 2, 1}); + assert_all_requirements_and_equals(flat_multiset(C{1, 1, 2, 3}, gt()), {3, 2, 1, 1}); + assert_all_requirements_and_equals(flat_set(sorted_unique, C{30000, 200, 1}, gt()), {30000, 200, 1}); + assert_all_requirements_and_equals(flat_multiset(sorted_equivalent, C{3, 3, -1}, gt()), {3, 3, -1}); + assert_all_requirements_and_equals(flat_set({30000, 200, 1}, gt()), {30000, 200, 1}); + assert_all_requirements_and_equals(flat_multiset({3, 3, -1}, gt()), {3, 3, -1}); + assert_all_requirements_and_equals(flat_set(sorted_unique, {30000, 200, 1}, gt()), {30000, 200, 1}); + assert_all_requirements_and_equals(flat_multiset(sorted_equivalent, {3, 3, -1}, gt()), {3, 3, -1}); + + flat_set a{}; + a = {1, 7, 7, 7, 2, 100, -1}; + assert_all_requirements_and_equals(a, {-1, 1, 2, 7, 100}); + flat_multiset b{}; + b = {1, 7, 7, 7, 2, 100, -1}; + assert_all_requirements_and_equals(b, {-1, 1, 2, 7, 7, 7, 100}); +} + +template +void test_spaceship_operator() { + static constexpr bool multi = _Is_specialization_v; + static constexpr bool invert = is_same_v>; + + T a{3, 2, 2, 1}; + T b{1, 2, 3}; + assert((a <=> b) == (multi ? (invert ? strong_ordering::greater : strong_ordering::less) : strong_ordering::equal)); + + T c{3, 2}; + assert((c <=> b) == (invert ? strong_ordering::less : strong_ordering::greater)); + + T d{5, 6, 7, 7, 8, 9}; + T e{5, 6, 7, 8, 100}; + assert((d <=> e) == strong_ordering::less); + + T f{1, 2, 3, 4}; + assert((f <=> a) == strong_ordering::greater); +} + +template +struct proxy_comparer { + bool operator()(const T& lhs, const T& rhs) const { + return m_less ? (lhs < rhs) : (lhs > rhs); + } + + bool m_less = true; +}; + +void test_non_static_comparer() { + flat_set> a{3, 2, 2, 1}; + assert_all_requirements_and_equals(a, {1, 2, 3}); + auto b = flat_set>({-1, 5, 9, 9, 9, 9, 9}, proxy_comparer{.m_less = false}); + assert_all_requirements_and_equals(b, {9, 5, -1}); + + auto aBackup = a; + a = b; + assert_all_requirements_and_equals(a, {9, 5, -1}); + a.insert_range(vector{7, 7, 3, 3, 2}); + assert_all_requirements_and_equals(a, {9, 7, 5, 3, 2, -1}); + + a = std::move(aBackup); + assert_all_requirements_and_equals(a, {1, 2, 3}); + + a.insert(-100); + assert_all_requirements_and_equals(a, {-100, 1, 2, 3}); + + a = b; + assert_all_requirements_and_equals(a, {9, 5, -1}); + + a.insert(7); + assert_all_requirements_and_equals(a, {9, 7, 5, -1}); +} + +int main() { + test_spaceship_operator>(); + test_spaceship_operator>(); + test_spaceship_operator>>(); + test_spaceship_operator>>(); + test_spaceship_operator, deque>>(); + test_spaceship_operator, deque>>(); + test_spaceship_operator, deque>>(); + test_spaceship_operator, deque>>(); + + test_constructors>(); + test_constructors>(); + + test_non_static_comparer(); + + assert_basic>(); + assert_basic, deque>>(); + + flat_multiset, deque> d; +} diff --git a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc index 70d0af803ec..51f0fd7365c 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc +++ b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc @@ -23,6 +23,7 @@ "execution", "expected", "filesystem", + "flat_set", "format", "forward_list", "fstream", diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index f9fa3090a20..fa11f9e9a79 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -29,6 +29,7 @@ import ; import ; import ; import ; +import ; import ; import ; import ; diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 276dd6fdd8d..8a626a8eeb0 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -876,6 +876,20 @@ STATIC_ASSERT(__cpp_lib_filesystem == 201703L); #endif #endif +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#ifndef __cpp_lib_flat_set +#error __cpp_lib_flat_set is not defined +#elif __cpp_lib_flat_set != 202207L +#error __cpp_lib_flat_set is not 202207L +#else +STATIC_ASSERT(__cpp_lib_flat_set == 202207L); +#endif +#else +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#error __cpp_lib_flat_set is defined +#endif +#endif + #ifdef __cpp_lib_concepts #ifndef __cpp_lib_format #error __cpp_lib_format is not defined diff --git a/tests/std/tests/include_each_header_alone_matrix.lst b/tests/std/tests/include_each_header_alone_matrix.lst index 9b041e051e5..0de7b470c6c 100644 --- a/tests/std/tests/include_each_header_alone_matrix.lst +++ b/tests/std/tests/include_each_header_alone_matrix.lst @@ -26,6 +26,7 @@ PM_CL="/DMEOW_HEADER=exception" PM_CL="/DMEOW_HEADER=execution" PM_CL="/DMEOW_HEADER=expected" PM_CL="/DMEOW_HEADER=filesystem" +PM_CL="/DMEOW_HEADER=flat_set" PM_CL="/DMEOW_HEADER=format" PM_CL="/DMEOW_HEADER=forward_list" PM_CL="/DMEOW_HEADER=fstream" From f6baf51619de12ace7dd2d16051d49ec716f13e8 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 1 Jul 2023 09:24:37 +0800 Subject: [PATCH 002/134] ``: Fix `erase_if` for `flat_set`/`flat_multiset` (#3833) Co-authored-by: Jakub Mazurkiewicz Co-authored-by: Stephan T. Lavavej --- stl/inc/flat_set | 26 +++++++++++++---------- tests/std/tests/P1222R4_flat_set/test.cpp | 25 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/stl/inc/flat_set b/stl/inc/flat_set index 67dc93c4da7..1bfb5e191f9 100644 --- a/stl/inc/flat_set +++ b/stl/inc/flat_set @@ -242,11 +242,9 @@ public: } _NODISCARD container_type extract() && { - // The container NEEDS to be cleared no matter what, - // which is not guaranteed by simply moving it away - // ("... valid but unspecified ...") container_type& _Cont = _Get_cont(); - _Clear_scope_guard _Guard{this}; + // always clears the container (N4950 [flat.set.modifiers]/14 and [flat.multiset.modifiers]/10) + _Clear_scope_guard<_Base_flat_set> _Guard{this}; container_type _Temp = _STD move(_Cont); return _Temp; } @@ -405,7 +403,7 @@ public: } private: - void inline _Assert_after_sorted_input() const { + void _Assert_after_sorted_input() const { _STL_ASSERT(_STD is_sorted(begin(), end(), _Get_comp()), "Input was not sorted!"); if constexpr (!_Multi) { _STL_ASSERT(_Is_unique(), "Input was not unique!"); @@ -624,15 +622,21 @@ public: }; _EXPORT_STD template -size_t erase_if(flat_set<_Kty, _Keylt, _Container>& _Val, _Pred _Predicate) { - _Clear_scope_guard _Guard{&_Val}; - return _Erase_remove_if(_Val, _Pass_fn(_Predicate)); +_Container::size_type erase_if(flat_set<_Kty, _Keylt, _Container>& _Val, _Pred _Predicate) { + // clears the container to maintain the invariants when an exception is thrown (N4950 [flat.set.erasure]/5) + _Clear_scope_guard> _Guard{_STD addressof(_Val)}; + const auto _Erased_count = _Erase_remove_if(_Val, _Pass_fn(_Predicate)); + _Guard._Clearable = nullptr; + return _Erased_count; } _EXPORT_STD template -size_t erase_if(flat_multiset<_Kty, _Keylt, _Container>& _Val, _Pred _Predicate) { - _Clear_scope_guard _Guard{&_Val}; - return _Erase_remove_if(_Val, _Pass_fn(_Predicate)); +_Container::size_type erase_if(flat_multiset<_Kty, _Keylt, _Container>& _Val, _Pred _Predicate) { + // clears the container to maintain the invariants when an exception is thrown (N4950 [flat.multiset.erasure]/5) + _Clear_scope_guard> _Guard{_STD addressof(_Val)}; + const auto _Erased_count = _Erase_remove_if(_Val, _Pass_fn(_Predicate)); + _Guard._Clearable = nullptr; + return _Erased_count; } template diff --git a/tests/std/tests/P1222R4_flat_set/test.cpp b/tests/std/tests/P1222R4_flat_set/test.cpp index 6a3ae2e1223..a7739cc7353 100644 --- a/tests/std/tests/P1222R4_flat_set/test.cpp +++ b/tests/std/tests/P1222R4_flat_set/test.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include #include #include #include @@ -190,6 +191,24 @@ void test_non_static_comparer() { assert_all_requirements_and_equals(a, {9, 7, 5, -1}); } +template +void test_extract() { + constexpr int elements[]{1, 2, 3, 4}; + C fs{1, 2, 3, 4}; + auto cont = std::move(fs).extract(); + assert(fs.empty()); + assert(ranges::equal(cont, elements)); +} + +template +void test_erase_if() { + constexpr int erased_result[]{1, 3}; + C fs{1, 2, 3, 4}; + erase_if(fs, [](int n) { return n % 2 == 0; }); + assert(fs.size() == 2); + assert(ranges::equal(fs, erased_result)); +} + int main() { test_spaceship_operator>(); test_spaceship_operator>(); @@ -205,6 +224,12 @@ int main() { test_non_static_comparer(); + test_extract>(); + test_extract>(); + + test_erase_if>(); + test_erase_if>(); + assert_basic>(); assert_basic, deque>>(); From 686123cbf71dcc33fe4a8116a322e996caf75ecc Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 27 Jul 2023 03:16:19 +0800 Subject: [PATCH 003/134] Drop `#pragma once` from `` (#3902) --- stl/inc/flat_set | 1 - 1 file changed, 1 deletion(-) diff --git a/stl/inc/flat_set b/stl/inc/flat_set index 1bfb5e191f9..dda412494cd 100644 --- a/stl/inc/flat_set +++ b/stl/inc/flat_set @@ -3,7 +3,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#pragma once #ifndef _FLAT_SET_ #define _FLAT_SET_ #include From 1b1e241d8ec30c0d5f3bff8edad93b369bbe9ebf Mon Sep 17 00:00:00 2001 From: achabense <60953653+achabense@users.noreply.github.com> Date: Sat, 9 Sep 2023 05:39:35 +0800 Subject: [PATCH 004/134] Various bugfixes / enhancements for `` (#3993) Co-authored-by: Stephan T. Lavavej --- stl/inc/flat_set | 315 ++++++++++++++-------- tests/std/tests/P1222R4_flat_set/test.cpp | 124 +++++++-- 2 files changed, 304 insertions(+), 135 deletions(-) diff --git a/stl/inc/flat_set b/stl/inc/flat_set index dda412494cd..7767ba9b951 100644 --- a/stl/inc/flat_set +++ b/stl/inc/flat_set @@ -16,6 +16,13 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C #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 @@ -58,10 +65,17 @@ public: static_assert(random_access_iterator, "The C++ Standard forbids containers without random " "access iterators from being adapted. See [flatset.overview]."); - _Base_flat_set() : _My_pair(_Zero_then_variadic_args_t{}, _Keylt()) {} + _Base_flat_set() : _My_pair(_Zero_then_variadic_args_t{}) {} + + template <_Allocator_for _Alloc> + _Base_flat_set(const _Deriv& _Set, const _Alloc& _Al) + : _My_pair(_One_then_variadic_args_t{}, _Set._Get_comp(), _Set._Get_cont(), _Al) {} + template <_Allocator_for _Alloc> + _Base_flat_set(_Deriv&& _Set, const _Alloc& _Al) + : _My_pair(_One_then_variadic_args_t{}, _STD move(_Set._Get_comp()), _STD move(_Set._Get_cont()), _Al) {} explicit _Base_flat_set(container_type _Cont, const key_compare& _Comp = key_compare()) - : _My_pair(_One_then_variadic_args_t{}, _STD move(_Cont), _Comp) { + : _My_pair(_One_then_variadic_args_t{}, _Comp, _STD move(_Cont)) { _Make_invariants_fulfilled(); } template <_Allocator_for _Alloc> @@ -71,7 +85,7 @@ public: : _Base_flat_set(container_type(_Cont, _Al), _Comp) {} _Base_flat_set(_Tsorted, container_type _Cont, const key_compare& _Comp = key_compare()) - : _My_pair(_One_then_variadic_args_t{}, _STD move(_Cont), _Comp) { + : _My_pair(_One_then_variadic_args_t{}, _Comp, _STD move(_Cont)) { _Assert_after_sorted_input(); } template <_Allocator_for _Alloc> @@ -81,11 +95,11 @@ public: _Base_flat_set(_Tsorted _Tsort, const container_type& _Cont, const key_compare& _Comp, const _Alloc& _Al) : _Base_flat_set(_Tsort, container_type(_Cont, _Al), _Comp) {} - explicit _Base_flat_set(const key_compare& _Comp) : _My_pair(_Zero_then_variadic_args_t{}, _Comp) {} + explicit _Base_flat_set(const key_compare& _Comp) : _My_pair(_One_then_variadic_args_t{}, _Comp) {} template <_Allocator_for _Alloc> - _Base_flat_set(const key_compare& _Comp, const _Alloc& _Al) : _Base_flat_set(_Comp, container_type(_Al)) {} + _Base_flat_set(const key_compare& _Comp, const _Alloc& _Al) : _My_pair(_One_then_variadic_args_t{}, _Comp, _Al) {} template <_Allocator_for _Alloc> - explicit _Base_flat_set(const _Alloc& _Al) : _Base_flat_set(container_type(_Al)) {} + explicit _Base_flat_set(const _Alloc& _Al) : _My_pair(_Zero_then_variadic_args_t{}, _Al) {} template _Base_flat_set(_Iter _First, _Iter _Last, const key_compare& _Comp = key_compare()) @@ -97,11 +111,14 @@ public: _Base_flat_set(_Iter _First, _Iter _Last, const _Alloc& _Al) : _Base_flat_set(container_type(_First, _Last, _Al)) {} template <_Container_compatible_range<_Kty> _Rng> - _Base_flat_set(from_range_t, _Rng&& _Range, const key_compare& _Comp = key_compare()) - : _Base_flat_set(container_type(from_range, _STD forward<_Rng>(_Range)), _Comp) {} + _Base_flat_set(from_range_t, _Rng&& _Range) + : _Base_flat_set(container_type(from_range, _STD forward<_Rng>(_Range))) {} template <_Container_compatible_range<_Kty> _Rng, _Allocator_for _Alloc> _Base_flat_set(from_range_t, _Rng&& _Range, const _Alloc& _Al) : _Base_flat_set(container_type(from_range, _STD forward<_Rng>(_Range), _Al)) {} + template <_Container_compatible_range<_Kty> _Rng> + _Base_flat_set(from_range_t, _Rng&& _Range, const key_compare& _Comp) + : _Base_flat_set(container_type(from_range, _STD forward<_Rng>(_Range)), _Comp) {} template <_Container_compatible_range<_Kty> _Rng, _Allocator_for _Alloc> _Base_flat_set(from_range_t, _Rng&& _Range, const key_compare& _Comp, const _Alloc& _Al) : _Base_flat_set(container_type(from_range, _STD forward<_Rng>(_Range), _Al), _Comp) {} @@ -117,7 +134,7 @@ public: : _Base_flat_set(_Tsort, container_type(_First, _Last, _Al)) {} _Base_flat_set(initializer_list<_Kty> _Ilist, const key_compare& _Comp = key_compare()) - : _Base_flat_set(_Ilist.begin(), _Ilist.end(), _Comp) {} + : _Base_flat_set(container_type(_Ilist.begin(), _Ilist.end()), _Comp) {} template <_Allocator_for _Alloc> _Base_flat_set(initializer_list<_Kty> _Ilist, const key_compare& _Comp, const _Alloc& _Al) : _Base_flat_set(container_type(_Ilist.begin(), _Ilist.end(), _Al), _Comp) {} @@ -126,7 +143,7 @@ public: : _Base_flat_set(container_type(_Ilist.begin(), _Ilist.end(), _Al)) {} _Base_flat_set(_Tsorted _Tsort, initializer_list<_Kty> _Ilist, const key_compare& _Comp = key_compare()) - : _Base_flat_set(_Tsort, _Ilist.begin(), _Ilist.end(), _Comp) {} + : _Base_flat_set(_Tsort, container_type(_Ilist.begin(), _Ilist.end()), _Comp) {} template <_Allocator_for _Alloc> _Base_flat_set(_Tsorted _Tsort, initializer_list<_Kty> _Ilist, const key_compare& _Comp, const _Alloc& _Al) : _Base_flat_set(_Tsort, container_type(_Ilist.begin(), _Ilist.end(), _Al), _Comp) {} @@ -135,11 +152,12 @@ public: : _Base_flat_set(_Tsort, container_type(_Ilist.begin(), _Ilist.end(), _Al)) {} _Deriv& operator=(initializer_list<_Kty> _Ilist) { - _Get_cont() = container_type(_Ilist.begin(), _Ilist.end()); + _Get_cont().assign(_Ilist.begin(), _Ilist.end()); _Make_invariants_fulfilled(); return static_cast<_Deriv&>(*this); } + // iterators _NODISCARD iterator begin() noexcept { return _Get_cont().begin(); } @@ -152,6 +170,7 @@ public: _NODISCARD const_iterator end() const noexcept { return _Get_cont().end(); } + _NODISCARD reverse_iterator rbegin() noexcept { return _Get_cont().rbegin(); } @@ -164,6 +183,7 @@ public: _NODISCARD const_reverse_iterator rend() const noexcept { return _Get_cont().rend(); } + _NODISCARD const_iterator cbegin() const noexcept { return _Get_cont().cbegin(); } @@ -177,6 +197,7 @@ public: return _Get_cont().crend(); } + // capacity _NODISCARD_EMPTY_MEMBER bool empty() const noexcept { return _Get_cont().empty(); } @@ -187,33 +208,36 @@ public: return _Get_cont().max_size(); } + // modifiers template auto emplace(_Args&&... _Vals) { - insert<_Kty>(_Kty{_STD forward<_Args>(_Vals)...}); + constexpr bool _Is_key_type = _In_place_key_extract_set<_Kty, remove_cvref_t<_Args>...>::_Extractable; + if constexpr (_Is_key_type) { + return _Emplace(_STD forward<_Args>(_Vals)...); + } else { + return _Emplace(_Kty(_STD forward<_Args>(_Vals)...)); + } } - template iterator emplace_hint(const_iterator _Hint, _Args&&... _Vals) { - return _Emplace_hint(_Hint, _Kty{_STD forward<_Args>(_Vals)...}); + constexpr bool _Is_key_type = _In_place_key_extract_set<_Kty, remove_cvref_t<_Args>...>::_Extractable; + if constexpr (_Is_key_type) { + return _Emplace_hint(_Hint, _STD forward<_Args>(_Vals)...); + } else { + return _Emplace_hint(_Hint, _Kty(_STD forward<_Args>(_Vals)...)); + } } auto insert(const value_type& _Val) { - return _Insert<_Kty>(_Val); + return _Emplace(_Val); } auto insert(value_type&& _Val) { - return _Insert(_STD move(_Val)); + return _Emplace(_STD move(_Val)); } - template requires (!_Multi && _Keylt_transparent && is_constructible_v<_Kty, _Other>) auto insert(_Other&& _Val) { - return _Insert(_STD forward<_Other>(_Val)); - } - - template - requires (!_Multi && _Keylt_transparent && is_constructible_v<_Kty, _Other>) - iterator insert(const_iterator _Hint, _Other&& _Val) { - return _Emplace_hint(_Hint, _STD forward<_Other>(_Val)); + return _Emplace(_STD forward<_Other>(_Val)); } iterator insert(const_iterator _Hint, const value_type& _Val) { @@ -222,17 +246,20 @@ public: iterator insert(const_iterator _Hint, value_type&& _Val) { return _Emplace_hint(_Hint, _STD move(_Val)); } + template + requires (!_Multi && _Keylt_transparent && is_constructible_v<_Kty, _Other>) + iterator insert(const_iterator _Hint, _Other&& _Val) { + return _Emplace_hint(_Hint, _STD forward<_Other>(_Val)); + } template - void insert(const _Iter& _First, const _Iter& _Last) { + void insert(_Iter _First, _Iter _Last) { _Insert_range(_First, _Last); } - template void insert(_Tsorted, _Iter _First, _Iter _Last) { _Insert_range(_First, _Last); } - template <_Container_compatible_range<_Kty> _Rng> void insert_range(_Rng&& _Range) { const size_type _Old_size = size(); @@ -240,14 +267,18 @@ public: _Restore_invariants_after_insert(_Old_size); } + void insert(initializer_list<_Kty> _Ilist) { + _Insert_range(_Ilist.begin(), _Ilist.end()); + } + void insert(_Tsorted, initializer_list<_Kty> _Ilist) { + _Insert_range(_Ilist.begin(), _Ilist.end()); + } + _NODISCARD container_type extract() && { - container_type& _Cont = _Get_cont(); // always clears the container (N4950 [flat.set.modifiers]/14 and [flat.multiset.modifiers]/10) _Clear_scope_guard<_Base_flat_set> _Guard{this}; - container_type _Temp = _STD move(_Cont); - return _Temp; + return _STD move(_Get_cont()); } - void replace(container_type&& _Cont) { _Get_cont() = _STD move(_Cont); _Assert_after_sorted_input(); @@ -262,13 +293,11 @@ public: size_type erase(const _Kty& _Val) { return _Erase(_Val); } - template requires _Keylt_transparent size_type erase(_Other&& _Val) { return _Erase(_STD forward<_Other>(_Val)); } - iterator erase(const_iterator _First, const_iterator _Last) { return _Get_cont().erase(_First, _Last); } @@ -277,11 +306,11 @@ public: _RANGES swap(_Get_comp(), _Other._Get_comp()); _RANGES swap(_Get_cont(), _Other._Get_cont()); } - void clear() noexcept { _Get_cont().clear(); } + // observers _NODISCARD key_compare key_comp() const { return _Get_comp(); } @@ -289,10 +318,10 @@ public: return _Get_comp(); } + // set operations _NODISCARD iterator find(const _Kty& _Val) { return _Find(_Val); } - _NODISCARD const_iterator find(const _Kty& _Val) const { return _Find(_Val); } @@ -302,7 +331,6 @@ public: _NODISCARD iterator find(const _Other& _Val) { return _Find(_Val); } - template requires _Keylt_transparent _NODISCARD const_iterator find(const _Other& _Val) const { @@ -313,7 +341,6 @@ public: const auto [_First, _Last] = equal_range(_Val); return _STD distance(_First, _Last); } - template requires _Keylt_transparent _NODISCARD size_type count(const _Other& _Val) const { @@ -322,13 +349,14 @@ public: } _NODISCARD bool contains(const _Kty& _Val) const { - return find(_Val) != end(); + return _STD binary_search(cbegin(), cend(), _Val, _Get_comp()); } template requires _Keylt_transparent _NODISCARD bool contains(const _Other& _Val) const { - return find(_Val) != end(); + return _STD binary_search(cbegin(), cend(), _Val, _Get_comp()); } + _NODISCARD iterator lower_bound(const _Kty& _Val) { return _STD lower_bound(begin(), end(), _Val, _Get_comp()); } @@ -341,7 +369,6 @@ public: _NODISCARD iterator lower_bound(const _Other& _Val) { return _STD lower_bound(begin(), end(), _Val, _Get_comp()); } - template requires _Keylt_transparent _NODISCARD const_iterator lower_bound(const _Other& _Val) const { @@ -351,7 +378,6 @@ public: _NODISCARD iterator upper_bound(const _Kty& _Val) { return _STD upper_bound(begin(), end(), _Val, _Get_comp()); } - _NODISCARD const_iterator upper_bound(const _Kty& _Val) const { return _STD upper_bound(cbegin(), cend(), _Val, _Get_comp()); } @@ -361,7 +387,6 @@ public: _NODISCARD iterator upper_bound(const _Other& _Val) { return _STD upper_bound(begin(), end(), _Val, _Get_comp()); } - template requires _Keylt_transparent _NODISCARD const_iterator upper_bound(const _Other& _Val) const { @@ -371,7 +396,6 @@ public: _NODISCARD pair equal_range(const _Kty& _Val) { return _STD equal_range(begin(), end(), _Val, _Get_comp()); } - _NODISCARD pair equal_range(const _Kty& _Val) const { return _STD equal_range(cbegin(), cend(), _Val, _Get_comp()); } @@ -381,7 +405,6 @@ public: _NODISCARD pair equal_range(const _Other& _Val) { return _STD equal_range(begin(), end(), _Val, _Get_comp()); } - template requires _Keylt_transparent _NODISCARD pair equal_range(const _Other& _Val) const { @@ -389,7 +412,7 @@ public: } _NODISCARD friend bool operator==(const _Deriv& _Lhs, const _Deriv& _Rhs) { - return _RANGES equal(_Lhs, _Rhs); + return _RANGES equal(_Lhs._Get_cont(), _Rhs._Get_cont()); } _NODISCARD friend _Synth_three_way_result<_Kty> operator<=>(const _Deriv& _Lhs, const _Deriv& _Rhs) { @@ -403,9 +426,9 @@ public: private: void _Assert_after_sorted_input() const { - _STL_ASSERT(_STD is_sorted(begin(), end(), _Get_comp()), "Input was not sorted!"); + _STL_ASSERT(_STD is_sorted(cbegin(), cend(), _Get_comp()), "Input was not sorted!"); if constexpr (!_Multi) { - _STL_ASSERT(_Is_unique(), "Input was not unique!"); + _STL_ASSERT(_Is_unique(), "Input was sorted but not unique!"); } } @@ -413,8 +436,8 @@ private: if (empty()) { return true; } - const_iterator _End = cend(); - const_iterator _It = begin(); + const const_iterator _End = cend(); + const_iterator _It = cbegin(); while (++_It != _End) { if (_Keys_equal(*(_It - 1), *_It)) { return false; @@ -423,64 +446,114 @@ private: return true; } + bool _Check_where(const const_iterator _Where, const _Kty& _Val) const { + // check that _Val can be inserted before _Where + const key_compare& _Compare = _Get_comp(); + if constexpr (_Multi) { + // check that _Where is the upper_bound for _Val + // equivalent to checking *(_Where-1) <= _Val < *_Where + return (_Where == cend() || _Compare(_Val, *_Where)) + && (_Where == cbegin() || !_Compare(_Val, *(_Where - 1))); + } else { + // check that _Where is the lower_bound for _Val, and *_Where is not equivalent to _Val + // equivalent to checking *(_Where-1) < _Val < *_Where + return (_Where == cend() || _Compare(_Val, *_Where)) + && (_Where == cbegin() || _Compare(*(_Where - 1), _Val)); + } + } + template - requires (_Keylt_transparent && is_constructible_v<_Kty, _Ty>) || is_same_v<_Ty, _Kty> - void _Emplace_hint(const_iterator _Where, _Ty&& _Val) { - _Container& _Cont = _Get_cont(); - _Keylt& _Compare = _Get_comp(); - const iterator _Begin = begin(); - const iterator _End = end(); + auto _Emplace(_Ty&& _Val) { + _Container& _Cont = _Get_cont(); + if constexpr (_Multi) { + _STL_INTERNAL_STATIC_ASSERT(is_same_v, _Kty>); + return _Cont.emplace(upper_bound(_Val), _STD forward<_Ty>(_Val)); + } else { + const iterator _End = end(); + const iterator _Where = lower_bound(_Val); + if (_Where != _End && _Keys_equal(*_Where, _Val)) { + return pair{_Where, false}; + } - if (_Where == _End || !_Compare(*_Where, _Val)) { - // _Val <= *_Where - // Left of _Where - if (_Where == _Begin || !_Compare(_Val, *(_Where - 1))) { - // _Val >= (*_Where - 1) - // Insert before _Where + if constexpr (is_same_v, _Kty>) { + return pair{_Cont.emplace(_Where, _STD forward<_Ty>(_Val)), true}; } else { - // _Val < (*_Where - 1) - _Where = _STD upper_bound(_Begin, _Where, _Val, _Compare); + // for flat_set::insert(auto&&) + _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent && is_constructible_v<_Kty, _Ty>); + _Kty _Keyval(_STD forward<_Ty>(_Val)); + _STL_ASSERT(_Check_where(_Where, _Keyval), "The input type was not equivalent to key_type!"); + return pair{_Cont.emplace(_Where, _STD move(_Keyval)), true}; + } + } + } + + template + iterator _Emplace_hint(const_iterator _Where, _Ty&& _Val) { + _Container& _Cont = _Get_cont(); + const key_compare& _Compare = _Get_comp(); + const const_iterator _Begin = cbegin(); + const const_iterator _End = cend(); + + if constexpr (_Multi) { + // look for the upper_bound for flat_multiset + if (_Where == _End || _Compare(_Val, *_Where)) { + // _Val < *_Where + if (_Where == _Begin || !_Compare(_Val, *(_Where - 1))) { + // _Val >= *(_Where-1) ~ upper_bound is _Where + } else { + // _Val < *(_Where-1) ~ upper_bound is in [_Begin,_Where-1] + _Where = _STD upper_bound(_Begin, _Where - 1, _Val); + } + } else { + // _Val >= *_Where ~ upper_bound is in [_Where+1,_End] + _Where = _STD upper_bound(_Where + 1, _End, _Val); } } else { - // _Val > *_Where - // Right of _Where - _Where = _STD lower_bound(_Where + 1, _End, _Val, _Compare); + // look for the lower_bound for flat_set + if (_Where == _End || !_Compare(*_Where, _Val)) { + // _Val <= *_Where + if (_Where == _Begin || _Compare(*(_Where - 1), _Val)) { + // _Val > *(_Where-1) ~ lower_bound is _Where + } else { + // _Val <= *(_Where-1) ~ lower_bound is in [_Begin,_Where-1] + _Where = _STD lower_bound(_Begin, _Where - 1, _Val); + } + } else { + // _Val > *_Where ~ lower_bound is in [_Where+1,_End] + _Where = _STD lower_bound(_Where + 1, _End, _Val); + } } if constexpr (_Multi) { - return _Cont.insert(_Where, _STD forward<_Ty>(_Val)); + _STL_INTERNAL_STATIC_ASSERT(is_same_v, _Kty>); + _STL_INTERNAL_CHECK(_Check_where(_Where, _Val)); + return _Cont.emplace(_Where, _STD forward<_Ty>(_Val)); } else { - if (_Where == _End || !_Keys_equal(_Val, *_Where)) { - return _Cont.insert(_Where, _STD forward<_Ty>(_Val)); + if (_Where != _End && _Keys_equal(_Val, *_Where)) { + return _Cont.begin() + (_Where - _Begin); + } + + if constexpr (is_same_v, _Kty>) { + _STL_INTERNAL_CHECK(_Check_where(_Where, _Val)); + return _Cont.emplace(_Where, _STD forward<_Ty>(_Val)); + } else { + // for flat_set::insert(hint,auto&&) + _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent && is_constructible_v<_Kty, _Ty>); + _Kty _Keyval(_STD forward<_Ty>(_Val)); + _STL_ASSERT(_Check_where(_Where, _Keyval), "The input type was not equivalent to key_type!"); + return _Cont.emplace(_Where, _STD move(_Keyval)); } - return _Where; } } template - void _Insert_range(_Iter _First, _Iter _Last) { + void _Insert_range(const _Iter _First, const _Iter _Last) { const size_type _Old_size = size(); _Container& _Cont = _Get_cont(); _Cont.insert(_Cont.end(), _First, _Last); _Restore_invariants_after_insert<_Presorted>(_Old_size); } - template - requires (!_Multi && _Keylt_transparent && is_constructible_v<_Kty, _Ty>) || is_same_v<_Ty, _Kty> - auto _Insert(_Ty&& _Val) { - _Container& _Cont = _Get_cont(); - const iterator _End = end(); - const iterator _Where = lower_bound(_Val); - if constexpr (_Multi) { - return _Cont.emplace(_Where, _STD forward<_Ty>(_Val)); - } else { - if (_Where != _End && _Keys_equal(*_Where, _Val)) { - return pair{_Where, false}; - } - return pair{_Cont.emplace(_Where, _STD forward<_Ty>(_Val)), true}; - } - } - template requires _Keylt_transparent || is_same_v<_Ty, _Kty> size_type _Erase(_Ty&& _Val) { @@ -506,8 +579,8 @@ private: template requires _Keylt_transparent || is_same_v<_Other, _Kty> _NODISCARD const_iterator _Find(const _Other& _Val) const { - const iterator _End = end(); - const iterator _Where = lower_bound(_Val); + const const_iterator _End = cend(); + const const_iterator _Where = lower_bound(_Val); if (_Where != _End && _Keys_equal(*_Where, _Val)) { return _Where; } else { @@ -522,33 +595,22 @@ private: return !_Compare(_Lhs, _Rhs) && !_Compare(_Rhs, _Lhs); } - // O(N) if already sorted. - void _Sort_potentially_sorted(const iterator& _Begin, const iterator& _End) { - key_compare& _Compare = _Get_comp(); - const iterator _Begin_unsorted = _STD is_sorted_until(_Begin, _End, _Compare); - - _STD sort(_Begin_unsorted, _End, _Compare); - - _STD inplace_merge(begin(), _Begin_unsorted, _End, _Compare); - } - void _Erase_dupes_if_needed() { if constexpr (!_Multi) { - iterator _End = end(); - iterator _New_end = + const iterator _End = end(); + const iterator _New_end = _STD unique(begin(), _End, [&](const _Kty& _Lhs, const _Kty& _Rhs) { return _Keys_equal(_Lhs, _Rhs); }); _Get_cont().erase(_New_end, _End); - } - if constexpr (!_Multi) { + _STL_INTERNAL_CHECK(_Is_unique()); } } template - void _Restore_invariants_after_insert(const size_type& _Old_size) { - key_compare& _Compare = _Get_comp(); - const iterator _Old_end = begin() + static_cast(_Old_size); - const iterator _New_end = end(); + void _Restore_invariants_after_insert(const size_type _Old_size) { + const key_compare& _Compare = _Get_comp(); + const iterator _Old_end = begin() + static_cast(_Old_size); + const iterator _New_end = end(); if constexpr (!_Presorted) { _STD sort(_Old_end, _New_end, _Compare); @@ -558,7 +620,7 @@ private: _STD inplace_merge(begin(), _Old_end, _New_end, _Compare); - _STL_INTERNAL_CHECK(_STD is_sorted(begin(), end(), _Get_comp())); + _STL_INTERNAL_CHECK(_STD is_sorted(begin(), end(), _Compare)); _Erase_dupes_if_needed(); } @@ -571,29 +633,35 @@ private: return; } - _Sort_potentially_sorted(_Begin, _End); - _STL_INTERNAL_CHECK(_STD is_sorted(begin(), end(), _Get_comp())); + // O(N) if already sorted. + const key_compare& _Compare = _Get_comp(); + const iterator _Begin_unsorted = _STD is_sorted_until(_Begin, _End, _Compare); + + _STD sort(_Begin_unsorted, _End, _Compare); + _STD inplace_merge(_Begin, _Begin_unsorted, _End, _Compare); + + _STL_INTERNAL_CHECK(_STD is_sorted(_Begin, _End, _Compare)); _Erase_dupes_if_needed(); } _NODISCARD const _Container& _Get_cont() const noexcept { - return _My_pair._Get_first(); + return _My_pair._Myval2; } _NODISCARD _Container& _Get_cont() noexcept { - return _My_pair._Get_first(); + return _My_pair._Myval2; } _NODISCARD const key_compare& _Get_comp() const noexcept { - return _My_pair._Myval2; + return _My_pair._Get_first(); } _NODISCARD key_compare& _Get_comp() noexcept { - return _My_pair._Myval2; + return _My_pair._Get_first(); } - _Compressed_pair _My_pair; + _Compressed_pair _My_pair; }; _EXPORT_STD struct sorted_unique_t { @@ -609,15 +677,23 @@ _EXPORT_STD inline constexpr sorted_equivalent_t sorted_equivalent{}; _EXPORT_STD template , class _Container = vector<_Kty>> class flat_set : public _Base_flat_set<_Kty, _Keylt, _Container, false, flat_set<_Kty, _Keylt, _Container>, sorted_unique_t> { +private: + using _Mybase = _Base_flat_set<_Kty, _Keylt, _Container, false, flat_set, sorted_unique_t>; + public: - using _Base_flat_set<_Kty, _Keylt, _Container, false, flat_set, sorted_unique_t>::_Base_flat_set; + using _Mybase::_Mybase; + using _Mybase::operator=; }; _EXPORT_STD template , class _Container = vector<_Kty>> class flat_multiset : public _Base_flat_set<_Kty, _Keylt, _Container, true, flat_multiset<_Kty, _Keylt, _Container>, sorted_equivalent_t> { +private: + using _Mybase = _Base_flat_set<_Kty, _Keylt, _Container, true, flat_multiset, sorted_equivalent_t>; + public: - using _Base_flat_set<_Kty, _Keylt, _Container, true, flat_multiset, sorted_equivalent_t>::_Base_flat_set; + using _Mybase::_Mybase; + using _Mybase::operator=; }; _EXPORT_STD template @@ -719,6 +795,11 @@ flat_multiset(sorted_equivalent_t, initializer_list<_Kty>, _Keylt = _Keylt()) -> _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_SET_ diff --git a/tests/std/tests/P1222R4_flat_set/test.cpp b/tests/std/tests/P1222R4_flat_set/test.cpp index a7739cc7353..7547d6887df 100644 --- a/tests/std/tests/P1222R4_flat_set/test.cpp +++ b/tests/std/tests/P1222R4_flat_set/test.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include using namespace std; @@ -65,10 +66,23 @@ void assert_reversible_container_requirements(const T& s) { static_assert(is_convertible_v); } +template +void test_ebco() { + // This tests an implementation-specific optimization. + using key_compare = T::key_compare; + using container_type = T::container_type; + if constexpr (is_empty_v && !is_final_v) { + static_assert(sizeof(container_type) == sizeof(T)); + } else { + static_assert(sizeof(container_type) < sizeof(T)); + } +} + template void assert_all_requirements_and_equals(const T& s, const initializer_list& il) { assert_container_requirements(s); assert_reversible_container_requirements(s); + test_ebco(); auto val_comp = s.value_comp(); auto begin_it = s.cbegin(); @@ -98,19 +112,6 @@ void assert_all_requirements_and_equals(const T& s, const initializer_list -void assert_basic() { - T s{3, 2, 2, 2, 1}; - assert_all_requirements_and_equals(s, {1, 2, 3}); - - s.insert(43); - assert_all_requirements_and_equals(s, {1, 2, 3, 43}); - - int my_ints[] = {1, 2, 3, 4, 55}; - s.insert_range(my_ints); - assert_all_requirements_and_equals(s, {1, 2, 3, 4, 43, 55}); -} - template void test_constructors() { using lt = std::less; @@ -132,9 +133,95 @@ void test_constructors() { flat_set a{}; a = {1, 7, 7, 7, 2, 100, -1}; assert_all_requirements_and_equals(a, {-1, 1, 2, 7, 100}); + assert_all_requirements_and_equals(flat_set(a, allocator{}), {-1, 1, 2, 7, 100}); + assert_all_requirements_and_equals(flat_set(std::move(a), allocator{}), {-1, 1, 2, 7, 100}); flat_multiset b{}; b = {1, 7, 7, 7, 2, 100, -1}; assert_all_requirements_and_equals(b, {-1, 1, 2, 7, 7, 7, 100}); + assert_all_requirements_and_equals(flat_multiset(b, allocator{}), {-1, 1, 2, 7, 7, 7, 100}); + assert_all_requirements_and_equals(flat_multiset(std::move(b), allocator{}), {-1, 1, 2, 7, 7, 7, 100}); +} + +template +void test_insert_1() { + using lt = std::less; + + const vector vec{0, 1, 2}; + { + flat_set a{5, 5}; + assert_all_requirements_and_equals(a, {5}); + a.emplace(); + assert_all_requirements_and_equals(a, {0, 5}); + a.emplace(1); + assert_all_requirements_and_equals(a, {0, 1, 5}); + a.insert(vec[2]); + assert_all_requirements_and_equals(a, {0, 1, 2, 5}); + a.insert(2); + assert_all_requirements_and_equals(a, {0, 1, 2, 5}); + a.insert(vec.rbegin(), vec.rend()); + assert_all_requirements_and_equals(a, {0, 1, 2, 5}); + a.insert(sorted_unique, vec.begin(), vec.end()); + assert_all_requirements_and_equals(a, {0, 1, 2, 5}); + a.insert_range(vec); + assert_all_requirements_and_equals(a, {0, 1, 2, 5}); + a.insert({6, 2, 3}); + assert_all_requirements_and_equals(a, {0, 1, 2, 3, 5, 6}); + a.insert(sorted_unique, {4, 5}); + assert_all_requirements_and_equals(a, {0, 1, 2, 3, 4, 5, 6}); + } + { + flat_multiset a{5, 5}; + assert_all_requirements_and_equals(a, {5, 5}); + a.emplace(); + assert_all_requirements_and_equals(a, {0, 5, 5}); + a.emplace(1); + assert_all_requirements_and_equals(a, {0, 1, 5, 5}); + a.insert(vec[2]); + assert_all_requirements_and_equals(a, {0, 1, 2, 5, 5}); + a.insert(2); + assert_all_requirements_and_equals(a, {0, 1, 2, 2, 5, 5}); + a.insert(vec.rbegin(), vec.rend()); + assert_all_requirements_and_equals(a, {0, 0, 1, 1, 2, 2, 2, 5, 5}); + a.insert(sorted_equivalent, vec.begin(), vec.end()); + assert_all_requirements_and_equals(a, {0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 5, 5}); + a.insert_range(vec); + assert_all_requirements_and_equals(a, {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 5, 5}); + a.insert({6, 2, 3}); + assert_all_requirements_and_equals(a, {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 5, 5, 6}); + a.insert(sorted_equivalent, {4, 5}); + assert_all_requirements_and_equals(a, {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 4, 5, 5, 5, 6}); + } +} + +template +void test_insert_2() { + using lt = std::less; + + const int val = 1; + { + flat_set a{0, 5}; + assert_all_requirements_and_equals(a, {0, 5}); + a.emplace_hint(a.end()); + assert_all_requirements_and_equals(a, {0, 5}); + a.emplace_hint(a.end(), 0); + assert_all_requirements_and_equals(a, {0, 5}); + a.insert(a.begin(), 6); + assert_all_requirements_and_equals(a, {0, 5, 6}); + a.insert(a.begin(), val); + assert_all_requirements_and_equals(a, {0, 1, 5, 6}); + } + { + flat_multiset a{0, 5}; + assert_all_requirements_and_equals(a, {0, 5}); + a.emplace_hint(a.end()); + assert_all_requirements_and_equals(a, {0, 0, 5}); + a.emplace_hint(a.end(), 0); + assert_all_requirements_and_equals(a, {0, 0, 0, 5}); + a.insert(a.begin(), 6); + assert_all_requirements_and_equals(a, {0, 0, 0, 5, 6}); + a.insert(a.begin(), val); + assert_all_requirements_and_equals(a, {0, 0, 0, 1, 5, 6}); + } } template @@ -191,6 +278,7 @@ void test_non_static_comparer() { assert_all_requirements_and_equals(a, {9, 7, 5, -1}); } + template void test_extract() { constexpr int elements[]{1, 2, 3, 4}; @@ -222,6 +310,11 @@ int main() { test_constructors>(); test_constructors>(); + test_insert_1>(); + test_insert_1>(); + test_insert_2>(); + test_insert_2>(); + test_non_static_comparer(); test_extract>(); @@ -229,9 +322,4 @@ int main() { test_erase_if>(); test_erase_if>(); - - assert_basic>(); - assert_basic, deque>>(); - - flat_multiset, deque> d; } From 8dd198ffa6528a3bb3fff015c0c90c056390b8ba Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 8 Sep 2023 14:44:35 -0700 Subject: [PATCH 005/134] Fix stealth merge conflict with C++20 Standard Library Modules. --- stl/modules/std.ixx | 2 ++ tests/std/include/test_header_units_and_modules.hpp | 4 ++++ .../std/tests/P1502R1_standard_library_header_units/test.cpp | 2 ++ 3 files changed, 8 insertions(+) diff --git a/stl/modules/std.ixx b/stl/modules/std.ixx index 07d18e66b2c..0d094093b5f 100644 --- a/stl/modules/std.ixx +++ b/stl/modules/std.ixx @@ -61,7 +61,9 @@ export module std; #include #endif // _HAS_CXX23 #include +#if _HAS_CXX23 #include +#endif // _HAS_CXX23 #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 06546ff448c..ce9482aac30 100644 --- a/tests/std/include/test_header_units_and_modules.hpp +++ b/tests/std/include/test_header_units_and_modules.hpp @@ -236,12 +236,14 @@ void test_filesystem() { assert(info.capacity != static_cast(-1)); } +#if TEST_STANDARD >= 23 void test_flat_set() { using namespace std; puts("Testing ."); // FIXME! ADD TEST COVERAGE HERE! } +#endif // TEST_STANDARD >= 23 void test_format() { using namespace std; @@ -1101,7 +1103,9 @@ void all_cpp_header_tests() { test_expected(); #endif // TEST_STANDARD >= 23 test_filesystem(); +#if TEST_STANDARD >= 23 test_flat_set(); +#endif // TEST_STANDARD >= 23 test_format(); test_forward_list(); test_fstream(); diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index 9ca66909b06..4362fd8e20f 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -31,7 +31,9 @@ import ; import ; #endif // TEST_STANDARD >= 23 import ; +#if TEST_STANDARD >= 23 import ; +#endif // TEST_STANDARD >= 23 import ; import ; import ; From 384040a9e1483a22483d1a35592468f0dbf93440 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 8 Sep 2023 14:49:18 -0700 Subject: [PATCH 006/134] Use 4-arg std::equal(). --- tests/std/tests/P1222R4_flat_set/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P1222R4_flat_set/test.cpp b/tests/std/tests/P1222R4_flat_set/test.cpp index 7547d6887df..cdbe4b7698f 100644 --- a/tests/std/tests/P1222R4_flat_set/test.cpp +++ b/tests/std/tests/P1222R4_flat_set/test.cpp @@ -97,7 +97,7 @@ void assert_all_requirements_and_equals(const T& s, const initializer_list Date: Tue, 19 Sep 2023 05:50:05 +0800 Subject: [PATCH 007/134] More bugfixes for `flat_set` (#4034) Co-authored-by: Stephan T. Lavavej --- stl/inc/flat_set | 21 ++++----- tests/std/tests/P1222R4_flat_set/test.cpp | 54 +++++++++++++++++++++++ 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/stl/inc/flat_set b/stl/inc/flat_set index 7767ba9b951..18fb0f2553b 100644 --- a/stl/inc/flat_set +++ b/stl/inc/flat_set @@ -294,9 +294,10 @@ public: return _Erase(_Val); } template - requires _Keylt_transparent + requires ( + _Keylt_transparent && !is_convertible_v<_Other, iterator> && !is_convertible_v<_Other, const_iterator>) size_type erase(_Other&& _Val) { - return _Erase(_STD forward<_Other>(_Val)); + return _Erase(_Val); } iterator erase(const_iterator _First, const_iterator _Last) { return _Get_cont().erase(_First, _Last); @@ -339,13 +340,13 @@ public: _NODISCARD size_type count(const _Kty& _Val) const { const auto [_First, _Last] = equal_range(_Val); - return _STD distance(_First, _Last); + return static_cast(_Last - _First); } template requires _Keylt_transparent _NODISCARD size_type count(const _Other& _Val) const { const auto [_First, _Last] = equal_range(_Val); - return _STD distance(_First, _Last); + return static_cast(_Last - _First); } _NODISCARD bool contains(const _Kty& _Val) const { @@ -502,11 +503,11 @@ private: // _Val >= *(_Where-1) ~ upper_bound is _Where } else { // _Val < *(_Where-1) ~ upper_bound is in [_Begin,_Where-1] - _Where = _STD upper_bound(_Begin, _Where - 1, _Val); + _Where = _STD upper_bound(_Begin, _Where - 1, _Val, _Compare); } } else { // _Val >= *_Where ~ upper_bound is in [_Where+1,_End] - _Where = _STD upper_bound(_Where + 1, _End, _Val); + _Where = _STD upper_bound(_Where + 1, _End, _Val, _Compare); } } else { // look for the lower_bound for flat_set @@ -516,11 +517,11 @@ private: // _Val > *(_Where-1) ~ lower_bound is _Where } else { // _Val <= *(_Where-1) ~ lower_bound is in [_Begin,_Where-1] - _Where = _STD lower_bound(_Begin, _Where - 1, _Val); + _Where = _STD lower_bound(_Begin, _Where - 1, _Val, _Compare); } } else { // _Val > *_Where ~ lower_bound is in [_Where+1,_End] - _Where = _STD lower_bound(_Where + 1, _End, _Val); + _Where = _STD lower_bound(_Where + 1, _End, _Val, _Compare); } } @@ -556,10 +557,10 @@ private: template requires _Keylt_transparent || is_same_v<_Ty, _Kty> - size_type _Erase(_Ty&& _Val) { + size_type _Erase(const _Ty& _Val) { const auto [_First, _Last] = equal_range(_Val); - const difference_type _Removed = _STD distance(_First, _Last); + const auto _Removed = static_cast(_Last - _First); _Get_cont().erase(_First, _Last); return _Removed; } diff --git a/tests/std/tests/P1222R4_flat_set/test.cpp b/tests/std/tests/P1222R4_flat_set/test.cpp index cdbe4b7698f..7422f959fca 100644 --- a/tests/std/tests/P1222R4_flat_set/test.cpp +++ b/tests/std/tests/P1222R4_flat_set/test.cpp @@ -222,6 +222,17 @@ void test_insert_2() { a.insert(a.begin(), val); assert_all_requirements_and_equals(a, {0, 0, 0, 1, 5, 6}); } + + // TRANSITION, too simple + using gt = std::greater; + { + flat_set a{0, 5}; + assert_all_requirements_and_equals(a, {5, 0}); + a.insert(a.begin(), 3); + assert_all_requirements_and_equals(a, {5, 3, 0}); + a.insert(a.end(), 4); + assert_all_requirements_and_equals(a, {5, 4, 3, 0}); + } } template @@ -288,6 +299,35 @@ void test_extract() { assert(ranges::equal(cont, elements)); } +// TRANSITION, too simple +void test_erase_1() { + flat_set fs{1}; + fs.erase(1); + assert_all_requirements_and_equals(fs, {}); +} + +template +struct holder { + T t; + operator T() && { + return std::move(t); + } +}; + +void test_erase_2() { + using C = flat_set>; + C fs{0, 1, 2, 3}; + assert_all_requirements_and_equals(fs, {0, 1, 2, 3}); + // this should be allowed per P2077R3: + fs.erase(holder{fs.cbegin()}); + assert_all_requirements_and_equals(fs, {1, 2, 3}); + fs.erase(holder{fs.begin()}); + assert_all_requirements_and_equals(fs, {2, 3}); + int i = 2; + fs.erase(ref(i)); + assert_all_requirements_and_equals(fs, {3}); +} + template void test_erase_if() { constexpr int erased_result[]{1, 3}; @@ -297,6 +337,15 @@ void test_erase_if() { assert(ranges::equal(fs, erased_result)); } +// TRANSITION, too simple +void test_count() { + flat_set fs{2}; + assert(fs.count(1) == 0); + + flat_multiset fs2{1, 2, 2, 3}; + assert(fs2.count(2) == 2); +} + int main() { test_spaceship_operator>(); test_spaceship_operator>(); @@ -320,6 +369,11 @@ int main() { test_extract>(); test_extract>(); + test_erase_1(); + test_erase_2(); + test_erase_if>(); test_erase_if>(); + + test_count(); } From bec079ac808a0b6f91a888c82ad657aeff8ed92e Mon Sep 17 00:00:00 2001 From: achabense <60953653+achabense@users.noreply.github.com> Date: Wed, 20 Sep 2023 03:03:53 +0800 Subject: [PATCH 008/134] More enhancements for `` (#4019) --- stl/inc/flat_set | 159 ++++++++++++++++++++++++++++------------------- 1 file changed, 94 insertions(+), 65 deletions(-) diff --git a/stl/inc/flat_set b/stl/inc/flat_set index 18fb0f2553b..d39368763f7 100644 --- a/stl/inc/flat_set +++ b/stl/inc/flat_set @@ -172,29 +172,29 @@ public: } _NODISCARD reverse_iterator rbegin() noexcept { - return _Get_cont().rbegin(); + return reverse_iterator(end()); } _NODISCARD const_reverse_iterator rbegin() const noexcept { - return _Get_cont().rbegin(); + return const_reverse_iterator(end()); } _NODISCARD reverse_iterator rend() noexcept { - return _Get_cont().rend(); + return reverse_iterator(begin()); } _NODISCARD const_reverse_iterator rend() const noexcept { - return _Get_cont().rend(); + return const_reverse_iterator(begin()); } _NODISCARD const_iterator cbegin() const noexcept { - return _Get_cont().cbegin(); + return begin(); } _NODISCARD const_iterator cend() const noexcept { - return _Get_cont().cend(); + return end(); } _NODISCARD const_reverse_iterator crbegin() const noexcept { - return _Get_cont().crbegin(); + return rbegin(); } _NODISCARD const_reverse_iterator crend() const noexcept { - return _Get_cont().crend(); + return rend(); } // capacity @@ -339,8 +339,12 @@ public: } _NODISCARD size_type count(const _Kty& _Val) const { - const auto [_First, _Last] = equal_range(_Val); - return static_cast(_Last - _First); + if constexpr (!_Multi) { + return contains(_Val); + } else { + const auto [_First, _Last] = equal_range(_Val); + return static_cast(_Last - _First); + } } template requires _Keylt_transparent @@ -350,66 +354,66 @@ public: } _NODISCARD bool contains(const _Kty& _Val) const { - return _STD binary_search(cbegin(), cend(), _Val, _Get_comp()); + return _STD binary_search(cbegin(), cend(), _Val, _Get_comp_v()); } template requires _Keylt_transparent _NODISCARD bool contains(const _Other& _Val) const { - return _STD binary_search(cbegin(), cend(), _Val, _Get_comp()); + return _STD binary_search(cbegin(), cend(), _Val, _Get_comp_v()); } _NODISCARD iterator lower_bound(const _Kty& _Val) { - return _STD lower_bound(begin(), end(), _Val, _Get_comp()); + return _STD lower_bound(begin(), end(), _Val, _Get_comp_v()); } _NODISCARD const_iterator lower_bound(const _Kty& _Val) const { - return _STD lower_bound(cbegin(), cend(), _Val, _Get_comp()); + return _STD lower_bound(cbegin(), cend(), _Val, _Get_comp_v()); } template requires _Keylt_transparent _NODISCARD iterator lower_bound(const _Other& _Val) { - return _STD lower_bound(begin(), end(), _Val, _Get_comp()); + return _STD lower_bound(begin(), end(), _Val, _Get_comp_v()); } template requires _Keylt_transparent _NODISCARD const_iterator lower_bound(const _Other& _Val) const { - return _STD lower_bound(cbegin(), cend(), _Val, _Get_comp()); + return _STD lower_bound(cbegin(), cend(), _Val, _Get_comp_v()); } _NODISCARD iterator upper_bound(const _Kty& _Val) { - return _STD upper_bound(begin(), end(), _Val, _Get_comp()); + return _STD upper_bound(begin(), end(), _Val, _Get_comp_v()); } _NODISCARD const_iterator upper_bound(const _Kty& _Val) const { - return _STD upper_bound(cbegin(), cend(), _Val, _Get_comp()); + return _STD upper_bound(cbegin(), cend(), _Val, _Get_comp_v()); } template requires _Keylt_transparent _NODISCARD iterator upper_bound(const _Other& _Val) { - return _STD upper_bound(begin(), end(), _Val, _Get_comp()); + return _STD upper_bound(begin(), end(), _Val, _Get_comp_v()); } template requires _Keylt_transparent _NODISCARD const_iterator upper_bound(const _Other& _Val) const { - return _STD upper_bound(cbegin(), cend(), _Val, _Get_comp()); + return _STD upper_bound(cbegin(), cend(), _Val, _Get_comp_v()); } _NODISCARD pair equal_range(const _Kty& _Val) { - return _STD equal_range(begin(), end(), _Val, _Get_comp()); + return _STD equal_range(begin(), end(), _Val, _Get_comp_v()); } _NODISCARD pair equal_range(const _Kty& _Val) const { - return _STD equal_range(cbegin(), cend(), _Val, _Get_comp()); + return _STD equal_range(cbegin(), cend(), _Val, _Get_comp_v()); } template requires _Keylt_transparent _NODISCARD pair equal_range(const _Other& _Val) { - return _STD equal_range(begin(), end(), _Val, _Get_comp()); + return _STD equal_range(begin(), end(), _Val, _Get_comp_v()); } template requires _Keylt_transparent _NODISCARD pair equal_range(const _Other& _Val) const { - return _STD equal_range(cbegin(), cend(), _Val, _Get_comp()); + return _STD equal_range(cbegin(), cend(), _Val, _Get_comp_v()); } _NODISCARD friend bool operator==(const _Deriv& _Lhs, const _Deriv& _Rhs) { @@ -427,7 +431,7 @@ public: private: void _Assert_after_sorted_input() const { - _STL_ASSERT(_STD is_sorted(cbegin(), cend(), _Get_comp()), "Input was not sorted!"); + _STL_ASSERT(_STD is_sorted(cbegin(), cend(), _Get_comp_v()), "Input was not sorted!"); if constexpr (!_Multi) { _STL_ASSERT(_Is_unique(), "Input was sorted but not unique!"); } @@ -449,7 +453,6 @@ private: bool _Check_where(const const_iterator _Where, const _Kty& _Val) const { // check that _Val can be inserted before _Where - const key_compare& _Compare = _Get_comp(); if constexpr (_Multi) { // check that _Where is the upper_bound for _Val // equivalent to checking *(_Where-1) <= _Val < *_Where @@ -472,7 +475,7 @@ private: } else { const iterator _End = end(); const iterator _Where = lower_bound(_Val); - if (_Where != _End && _Keys_equal(*_Where, _Val)) { + if (_Where != _End && !_Compare(_Val, *_Where)) { return pair{_Where, false}; } @@ -491,7 +494,7 @@ private: template iterator _Emplace_hint(const_iterator _Where, _Ty&& _Val) { _Container& _Cont = _Get_cont(); - const key_compare& _Compare = _Get_comp(); + auto _Comp = _Get_comp_v(); const const_iterator _Begin = cbegin(); const const_iterator _End = cend(); @@ -503,11 +506,11 @@ private: // _Val >= *(_Where-1) ~ upper_bound is _Where } else { // _Val < *(_Where-1) ~ upper_bound is in [_Begin,_Where-1] - _Where = _STD upper_bound(_Begin, _Where - 1, _Val, _Compare); + _Where = _STD upper_bound(_Begin, _Where - 1, _Val, _Comp); } } else { // _Val >= *_Where ~ upper_bound is in [_Where+1,_End] - _Where = _STD upper_bound(_Where + 1, _End, _Val, _Compare); + _Where = _STD upper_bound(_Where + 1, _End, _Val, _Comp); } } else { // look for the lower_bound for flat_set @@ -517,11 +520,11 @@ private: // _Val > *(_Where-1) ~ lower_bound is _Where } else { // _Val <= *(_Where-1) ~ lower_bound is in [_Begin,_Where-1] - _Where = _STD lower_bound(_Begin, _Where - 1, _Val, _Compare); + _Where = _STD lower_bound(_Begin, _Where - 1, _Val, _Comp); } } else { // _Val > *_Where ~ lower_bound is in [_Where+1,_End] - _Where = _STD lower_bound(_Where + 1, _End, _Val, _Compare); + _Where = _STD lower_bound(_Where + 1, _End, _Val, _Comp); } } @@ -530,7 +533,7 @@ private: _STL_INTERNAL_CHECK(_Check_where(_Where, _Val)); return _Cont.emplace(_Where, _STD forward<_Ty>(_Val)); } else { - if (_Where != _End && _Keys_equal(_Val, *_Where)) { + if (_Where != _End && !_Compare(_Val, *_Where)) { return _Cont.begin() + (_Where - _Begin); } @@ -556,46 +559,51 @@ private: } template - requires _Keylt_transparent || is_same_v<_Ty, _Kty> size_type _Erase(const _Ty& _Val) { - const auto [_First, _Last] = equal_range(_Val); + _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent || is_same_v<_Ty, _Kty>); - const auto _Removed = static_cast(_Last - _First); - _Get_cont().erase(_First, _Last); - return _Removed; + if constexpr (!_Multi && is_same_v<_Ty, _Kty>) { + const iterator _Where = lower_bound(_Val); + if (_Where != end() && !_Compare(_Val, *_Where)) { + _Get_cont().erase(_Where); + return 1; + } + return 0; + } else { + const auto [_First, _Last] = equal_range(_Val); + + const auto _Removed = static_cast(_Last - _First); + _Get_cont().erase(_First, _Last); + return _Removed; + } } - template - requires _Keylt_transparent || is_same_v<_Other, _Kty> - _NODISCARD iterator _Find(const _Other& _Val) { + template + _NODISCARD iterator _Find(const _Ty& _Val) { + _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent || is_same_v<_Ty, _Kty>); + const iterator _End = end(); const iterator _Where = lower_bound(_Val); - if (_Where != _End && _Keys_equal(*_Where, _Val)) { + if (_Where != _End && !_Compare(_Val, *_Where)) { return _Where; } else { return _End; } } - template - requires _Keylt_transparent || is_same_v<_Other, _Kty> - _NODISCARD const_iterator _Find(const _Other& _Val) const { + template + _NODISCARD const_iterator _Find(const _Ty& _Val) const { + _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent || is_same_v<_Ty, _Kty>); + const const_iterator _End = cend(); const const_iterator _Where = lower_bound(_Val); - if (_Where != _End && _Keys_equal(*_Where, _Val)) { + if (_Where != _End && !_Compare(_Val, *_Where)) { return _Where; } else { return _End; } } - template - requires _Keylt_transparent || (is_same_v<_Kty, _Lhty> && is_same_v<_Lhty, _Rhty>) - _NODISCARD bool _Keys_equal(const _Lhty& _Lhs, const _Rhty& _Rhs) const { - const key_compare& _Compare = _Get_comp(); - return !_Compare(_Lhs, _Rhs) && !_Compare(_Rhs, _Lhs); - } - void _Erase_dupes_if_needed() { if constexpr (!_Multi) { const iterator _End = end(); @@ -609,19 +617,20 @@ private: template void _Restore_invariants_after_insert(const size_type _Old_size) { - const key_compare& _Compare = _Get_comp(); - const iterator _Old_end = begin() + static_cast(_Old_size); - const iterator _New_end = end(); + auto _Comp = _Get_comp_v(); + const iterator _Begin = begin(); + const iterator _Old_end = _Begin + static_cast(_Old_size); + const iterator _New_end = end(); if constexpr (!_Presorted) { - _STD sort(_Old_end, _New_end, _Compare); + _STD sort(_Old_end, _New_end, _Comp); } else { - _STL_ASSERT(_STD is_sorted(_Old_end, _New_end, _Compare), "Input was not sorted!"); + _STL_ASSERT(_STD is_sorted(_Old_end, _New_end, _Comp), "Input was not sorted!"); } - _STD inplace_merge(begin(), _Old_end, _New_end, _Compare); + _STD inplace_merge(_Begin, _Old_end, _New_end, _Comp); - _STL_INTERNAL_CHECK(_STD is_sorted(begin(), end(), _Compare)); + _STL_INTERNAL_CHECK(_STD is_sorted(_Begin, _New_end, _Comp)); _Erase_dupes_if_needed(); } @@ -635,17 +644,33 @@ private: } // O(N) if already sorted. - const key_compare& _Compare = _Get_comp(); - const iterator _Begin_unsorted = _STD is_sorted_until(_Begin, _End, _Compare); + auto _Comp = _Get_comp_v(); + const iterator _Begin_unsorted = _STD is_sorted_until(_Begin, _End, _Comp); - _STD sort(_Begin_unsorted, _End, _Compare); - _STD inplace_merge(_Begin, _Begin_unsorted, _End, _Compare); + _STD sort(_Begin_unsorted, _End, _Comp); + _STD inplace_merge(_Begin, _Begin_unsorted, _End, _Comp); - _STL_INTERNAL_CHECK(_STD is_sorted(_Begin, _End, _Compare)); + _STL_INTERNAL_CHECK(_STD is_sorted(_Begin, _End, _Comp)); _Erase_dupes_if_needed(); } + template + _NODISCARD bool _Compare(const _Lty& _Lhs, const _Rty& _Rhs) const + noexcept(noexcept(_DEBUG_LT_PRED(_My_pair._Get_first(), _Lhs, _Rhs))) { + _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent || (is_same_v<_Kty, _Lty> && is_same_v<_Kty, _Rty>) ); + + return _DEBUG_LT_PRED(_My_pair._Get_first(), _Lhs, _Rhs); + } + + template + _NODISCARD bool _Keys_equal(const _Lty& _Lhs, const _Rty& _Rhs) const + noexcept(noexcept(!_Compare(_Lhs, _Rhs) && !_Compare(_Rhs, _Lhs))) { + _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent || (is_same_v<_Kty, _Lty> && is_same_v<_Kty, _Rty>) ); + + return !_Compare(_Lhs, _Rhs) && !_Compare(_Rhs, _Lhs); + } + _NODISCARD const _Container& _Get_cont() const noexcept { return _My_pair._Myval2; } @@ -662,6 +687,10 @@ private: return _My_pair._Get_first(); } + _NODISCARD auto _Get_comp_v() const noexcept { + return _STD _Pass_fn(_My_pair._Get_first()); + } + _Compressed_pair _My_pair; }; From e23a4e2a05eeaea9cc93b640828288080a3b7ad2 Mon Sep 17 00:00:00 2001 From: achabense <60953653+achabense@users.noreply.github.com> Date: Thu, 28 Sep 2023 04:07:34 +0800 Subject: [PATCH 009/134] Fixes for `flat_set` (#4050) Co-authored-by: Stephan T. Lavavej --- stl/inc/flat_set | 141 +++++++++++----------- tests/std/tests/P1222R4_flat_set/test.cpp | 126 ++++++++++++++++++- 2 files changed, 197 insertions(+), 70 deletions(-) diff --git a/stl/inc/flat_set b/stl/inc/flat_set index d39368763f7..1efe93be36c 100644 --- a/stl/inc/flat_set +++ b/stl/inc/flat_set @@ -25,16 +25,21 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN -template -struct _NODISCARD _Clear_scope_guard { - _Ty* _Clearable; - ~_Clear_scope_guard() { - if (_Clearable) { - _Clearable->clear(); +template +struct _NODISCARD _Clear_guard { + _Ty* _Target; + ~_Clear_guard() { + if (_Target) { + _Target->clear(); } } }; +template +struct [[maybe_unused]] _NODISCARD _Clear_guard<_Ty, true> { + _Ty* _Target; // do nothing as the guarded operations don't throw. +}; + template concept _Allocator_for = uses_allocator_v<_Container, _Alloc>; @@ -86,7 +91,7 @@ public: _Base_flat_set(_Tsorted, container_type _Cont, const key_compare& _Comp = key_compare()) : _My_pair(_One_then_variadic_args_t{}, _Comp, _STD move(_Cont)) { - _Assert_after_sorted_input(); + _STL_ASSERT(_Check_sorted(cbegin(), cend()), _Msg_not_sorted); } template <_Allocator_for _Alloc> _Base_flat_set(_Tsorted _Tsort, const container_type& _Cont, const _Alloc& _Al) @@ -152,8 +157,10 @@ public: : _Base_flat_set(_Tsort, container_type(_Ilist.begin(), _Ilist.end(), _Al)) {} _Deriv& operator=(initializer_list<_Kty> _Ilist) { + _Clear_guard<_Base_flat_set> _Guard{this}; _Get_cont().assign(_Ilist.begin(), _Ilist.end()); _Make_invariants_fulfilled(); + _Guard._Target = nullptr; return static_cast<_Deriv&>(*this); } @@ -228,25 +235,25 @@ public: } } - auto insert(const value_type& _Val) { + auto insert(const _Kty& _Val) { return _Emplace(_Val); } - auto insert(value_type&& _Val) { + auto insert(_Kty&& _Val) { return _Emplace(_STD move(_Val)); } - template + template <_Different_from<_Kty> _Other> requires (!_Multi && _Keylt_transparent && is_constructible_v<_Kty, _Other>) auto insert(_Other&& _Val) { return _Emplace(_STD forward<_Other>(_Val)); } - iterator insert(const_iterator _Hint, const value_type& _Val) { + iterator insert(const_iterator _Hint, const _Kty& _Val) { return _Emplace_hint(_Hint, _Val); } - iterator insert(const_iterator _Hint, value_type&& _Val) { + iterator insert(const_iterator _Hint, _Kty&& _Val) { return _Emplace_hint(_Hint, _STD move(_Val)); } - template + template <_Different_from<_Kty> _Other> requires (!_Multi && _Keylt_transparent && is_constructible_v<_Kty, _Other>) iterator insert(const_iterator _Hint, _Other&& _Val) { return _Emplace_hint(_Hint, _STD forward<_Other>(_Val)); @@ -263,7 +270,15 @@ public: template <_Container_compatible_range<_Kty> _Rng> void insert_range(_Rng&& _Range) { const size_type _Old_size = size(); - _Get_cont().append_range(_STD forward<_Rng>(_Range)); + + _Container& _Cont = _Get_cont(); + if constexpr (requires { _Cont.append_range(_STD forward<_Rng>(_Range)); }) { + _Cont.append_range(_STD forward<_Rng>(_Range)); + } else { + for (const auto& _Val : _Range) { + _Cont.insert(_Cont.end(), _Val); + } + } _Restore_invariants_after_insert(_Old_size); } @@ -274,14 +289,16 @@ public: _Insert_range(_Ilist.begin(), _Ilist.end()); } - _NODISCARD container_type extract() && { + _NODISCARD container_type extract() && noexcept(is_nothrow_move_constructible_v<_Container>) /* strengthened */ { // always clears the container (N4950 [flat.set.modifiers]/14 and [flat.multiset.modifiers]/10) - _Clear_scope_guard<_Base_flat_set> _Guard{this}; + _Clear_guard<_Base_flat_set, is_nothrow_move_constructible_v<_Container>> _Guard{this}; return _STD move(_Get_cont()); } void replace(container_type&& _Cont) { - _Get_cont() = _STD move(_Cont); - _Assert_after_sorted_input(); + _STL_ASSERT(_Check_sorted(_Cont.cbegin(), _Cont.cend()), _Msg_not_sorted); + _Clear_guard<_Base_flat_set, is_nothrow_move_assignable_v<_Container>> _Guard{this}; + _Get_cont() = _STD move(_Cont); + _Guard._Target = nullptr; } iterator erase(iterator _Where) { @@ -293,7 +310,7 @@ public: size_type erase(const _Kty& _Val) { return _Erase(_Val); } - template + template <_Different_from<_Kty> _Other> requires ( _Keylt_transparent && !is_convertible_v<_Other, iterator> && !is_convertible_v<_Other, const_iterator>) size_type erase(_Other&& _Val) { @@ -339,11 +356,11 @@ public: } _NODISCARD size_type count(const _Kty& _Val) const { - if constexpr (!_Multi) { - return contains(_Val); - } else { + if constexpr (_Multi) { const auto [_First, _Last] = equal_range(_Val); return static_cast(_Last - _First); + } else { + return contains(_Val); } } template @@ -420,7 +437,7 @@ public: return _RANGES equal(_Lhs._Get_cont(), _Rhs._Get_cont()); } - _NODISCARD friend _Synth_three_way_result<_Kty> operator<=>(const _Deriv& _Lhs, const _Deriv& _Rhs) { + _NODISCARD friend auto operator<=>(const _Deriv& _Lhs, const _Deriv& _Rhs) { return _STD lexicographical_compare_three_way( _Lhs.cbegin(), _Lhs.cend(), _Rhs.cbegin(), _Rhs.cend(), _Synth_three_way{}); } @@ -430,28 +447,26 @@ public: } private: - void _Assert_after_sorted_input() const { - _STL_ASSERT(_STD is_sorted(cbegin(), cend(), _Get_comp_v()), "Input was not sorted!"); - if constexpr (!_Multi) { - _STL_ASSERT(_Is_unique(), "Input was sorted but not unique!"); - } - } - - bool _Is_unique() const { - if (empty()) { - return true; - } - const const_iterator _End = cend(); - const_iterator _It = cbegin(); - while (++_It != _End) { - if (_Keys_equal(*(_It - 1), *_It)) { - return false; + _NODISCARD bool _Check_sorted(const_iterator _It, const const_iterator _End) const { + if constexpr (_Multi) { + return _STD is_sorted(_It, _End, _Get_comp_v()); + } else { + // sorted-unique + if (_It == _End) { + return true; } + while (++_It != _End) { + if (!_Compare(*(_It - 1), *_It)) { + return false; + } + } + return true; } - return true; } - bool _Check_where(const const_iterator _Where, const _Kty& _Val) const { + static constexpr const char* _Msg_not_sorted = _Multi ? "Input was not sorted!" : "Input was not sorted-unique!"; + + _NODISCARD bool _Check_where(const const_iterator _Where, const _Kty& _Val) const { // check that _Val can be inserted before _Where if constexpr (_Multi) { // check that _Where is the upper_bound for _Val @@ -604,14 +619,14 @@ private: } } - void _Erase_dupes_if_needed() { + void _Erase_dupes_if_not_multi() { if constexpr (!_Multi) { - const iterator _End = end(); - const iterator _New_end = - _STD unique(begin(), _End, [&](const _Kty& _Lhs, const _Kty& _Rhs) { return _Keys_equal(_Lhs, _Rhs); }); + const auto _Equal_to = [this](const _Kty& _Lhs, const _Kty& _Rhs) { + return !_Compare(_Lhs, _Rhs) && !_Compare(_Rhs, _Lhs); + }; + const iterator _End = end(); + const iterator _New_end = _STD unique(begin(), _End, _Equal_to); _Get_cont().erase(_New_end, _End); - - _STL_INTERNAL_CHECK(_Is_unique()); } } @@ -625,14 +640,13 @@ private: if constexpr (!_Presorted) { _STD sort(_Old_end, _New_end, _Comp); } else { - _STL_ASSERT(_STD is_sorted(_Old_end, _New_end, _Comp), "Input was not sorted!"); + _STL_ASSERT(_Check_sorted(_Old_end, _New_end), _Msg_not_sorted); } _STD inplace_merge(_Begin, _Old_end, _New_end, _Comp); + _Erase_dupes_if_not_multi(); - _STL_INTERNAL_CHECK(_STD is_sorted(_Begin, _New_end, _Comp)); - - _Erase_dupes_if_needed(); + _STL_INTERNAL_CHECK(_Check_sorted(cbegin(), cend())); } void _Make_invariants_fulfilled() { @@ -649,10 +663,9 @@ private: _STD sort(_Begin_unsorted, _End, _Comp); _STD inplace_merge(_Begin, _Begin_unsorted, _End, _Comp); + _Erase_dupes_if_not_multi(); - _STL_INTERNAL_CHECK(_STD is_sorted(_Begin, _End, _Comp)); - - _Erase_dupes_if_needed(); + _STL_INTERNAL_CHECK(_Check_sorted(cbegin(), cend())); } template @@ -663,14 +676,6 @@ private: return _DEBUG_LT_PRED(_My_pair._Get_first(), _Lhs, _Rhs); } - template - _NODISCARD bool _Keys_equal(const _Lty& _Lhs, const _Rty& _Rhs) const - noexcept(noexcept(!_Compare(_Lhs, _Rhs) && !_Compare(_Rhs, _Lhs))) { - _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent || (is_same_v<_Kty, _Lty> && is_same_v<_Kty, _Rty>) ); - - return !_Compare(_Lhs, _Rhs) && !_Compare(_Rhs, _Lhs); - } - _NODISCARD const _Container& _Get_cont() const noexcept { return _My_pair._Myval2; } @@ -729,18 +734,18 @@ public: _EXPORT_STD template _Container::size_type erase_if(flat_set<_Kty, _Keylt, _Container>& _Val, _Pred _Predicate) { // clears the container to maintain the invariants when an exception is thrown (N4950 [flat.set.erasure]/5) - _Clear_scope_guard> _Guard{_STD addressof(_Val)}; - const auto _Erased_count = _Erase_remove_if(_Val, _Pass_fn(_Predicate)); - _Guard._Clearable = nullptr; + _Clear_guard> _Guard{_STD addressof(_Val)}; + const auto _Erased_count = _STD _Erase_remove_if(_Val, _STD _Pass_fn(_Predicate)); + _Guard._Target = nullptr; return _Erased_count; } _EXPORT_STD template _Container::size_type erase_if(flat_multiset<_Kty, _Keylt, _Container>& _Val, _Pred _Predicate) { // clears the container to maintain the invariants when an exception is thrown (N4950 [flat.multiset.erasure]/5) - _Clear_scope_guard> _Guard{_STD addressof(_Val)}; - const auto _Erased_count = _Erase_remove_if(_Val, _Pass_fn(_Predicate)); - _Guard._Clearable = nullptr; + _Clear_guard> _Guard{_STD addressof(_Val)}; + const auto _Erased_count = _STD _Erase_remove_if(_Val, _STD _Pass_fn(_Predicate)); + _Guard._Target = nullptr; return _Erased_count; } diff --git a/tests/std/tests/P1222R4_flat_set/test.cpp b/tests/std/tests/P1222R4_flat_set/test.cpp index 7422f959fca..9bb2adcd0df 100644 --- a/tests/std/tests/P1222R4_flat_set/test.cpp +++ b/tests/std/tests/P1222R4_flat_set/test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include using namespace std; @@ -235,6 +236,124 @@ void test_insert_2() { } } +struct key_comparer { + const auto& extract_key(const auto& obj) const { + if constexpr (requires { obj.key; }) { + return obj.key; + } else { + return obj; + } + } + + bool operator()(const auto& lhs, const auto& rhs) const { + return extract_key(lhs) < extract_key(rhs); + } + + using is_transparent = int; +}; + +void test_comparer_application() { + // The set must rely on its comparer to do the comparisons. + struct incomparable { + int key; + bool operator<(const incomparable&) const = delete; + bool operator==(const incomparable&) const = delete; + }; + + flat_set fs{{0}, {3}, {1}, {0}, {5}}; + assert(fs.contains(0)); + assert(!fs.contains(2)); + fs.insert(fs.begin(), incomparable{4}); + fs.insert(2); + assert(fs.contains(4)); + assert(fs.contains(incomparable{2})); + + assert(fs.lower_bound(3) == fs.lower_bound(incomparable{3})); + fs.erase(2); + assert(!fs.contains(incomparable{2})); +} + +void test_insert_transparent() { + // For flat_set::insert([hint,]auto&&), the input should be unchanged if the set already + // contains an equivalent element. + struct detect_conversion { + int key; + mutable bool converted = false; + + explicit operator int() const { + converted = true; + return key; + } + }; + + flat_set fs{0, 3, 5}; + assert_all_requirements_and_equals(fs, {0, 3, 5}); + detect_conversion detector{3}; + + assert(!detector.converted); + fs.insert(detector /*3*/); + assert_all_requirements_and_equals(fs, {0, 3, 5}); + assert(!detector.converted); + + detector.key = 1; + + assert(!detector.converted); + fs.insert(detector /*1*/); + assert_all_requirements_and_equals(fs, {0, 1, 3, 5}); + assert(detector.converted); + + detector.converted = false; + + assert(!detector.converted); + fs.insert(fs.end(), detector /*1*/); + assert_all_requirements_and_equals(fs, {0, 1, 3, 5}); + assert(!detector.converted); + + detector.key = 2; + + assert(!detector.converted); + fs.insert(fs.begin(), detector /*2*/); + assert_all_requirements_and_equals(fs, {0, 1, 2, 3, 5}); + assert(detector.converted); +} + +void test_insert_using_invalid_hint() { + mt19937 eng(42); + + uniform_int_distribution dist_seq(0, 20); + + vector seq(200); + for (int& val : seq) { + val = dist_seq(eng); + } + + { + flat_multiset with_hint; + flat_multiset no_hint; + for (const int val : seq) { + uniform_int_distribution dist_idx(0, static_cast(with_hint.size())); + auto random_hint = with_hint.begin() + dist_idx(eng); + with_hint.insert(random_hint, val); + no_hint.insert(val); + } + + assert(with_hint == no_hint); + } + + { + flat_set with_hint; + flat_set no_hint; + for (const int val : seq) { + uniform_int_distribution dist_idx(0, static_cast(with_hint.size())); + auto random_hint = with_hint.begin() + dist_idx(eng); + with_hint.insert(random_hint, val); + no_hint.insert(val); + } + + assert(with_hint == no_hint); + } +} + template void test_spaceship_operator() { static constexpr bool multi = _Is_specialization_v; @@ -342,8 +461,8 @@ void test_count() { flat_set fs{2}; assert(fs.count(1) == 0); - flat_multiset fs2{1, 2, 2, 3}; - assert(fs2.count(2) == 2); + flat_multiset fs2{10, 20, 20, 30}; + assert(fs2.count(20) == 2); } int main() { @@ -363,7 +482,10 @@ int main() { test_insert_1>(); test_insert_2>(); test_insert_2>(); + test_insert_transparent(); + test_insert_using_invalid_hint(); + test_comparer_application(); test_non_static_comparer(); test_extract>(); From b37931c69cb9ee1f7a650f5957e8c2faeece08e0 Mon Sep 17 00:00:00 2001 From: achabense <60953653+achabense@users.noreply.github.com> Date: Tue, 3 Oct 2023 09:20:38 +0800 Subject: [PATCH 010/134] Cleanups for `` (#4064) Co-authored-by: Stephan T. Lavavej --- stl/inc/flat_set | 340 +++++++++++----------- tests/std/tests/P1222R4_flat_set/test.cpp | 95 ++++-- 2 files changed, 240 insertions(+), 195 deletions(-) diff --git a/stl/inc/flat_set b/stl/inc/flat_set index 1efe93be36c..a8c5cd92748 100644 --- a/stl/inc/flat_set +++ b/stl/inc/flat_set @@ -25,7 +25,7 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN -template +template struct _NODISCARD _Clear_guard { _Ty* _Target; ~_Clear_guard() { @@ -35,11 +35,6 @@ struct _NODISCARD _Clear_guard { } }; -template -struct [[maybe_unused]] _NODISCARD _Clear_guard<_Ty, true> { - _Ty* _Target; // do nothing as the guarded operations don't throw. -}; - template concept _Allocator_for = uses_allocator_v<_Container, _Alloc>; @@ -70,17 +65,17 @@ public: static_assert(random_access_iterator, "The C++ Standard forbids containers without random " "access iterators from being adapted. See [flatset.overview]."); - _Base_flat_set() : _My_pair(_Zero_then_variadic_args_t{}) {} + _Base_flat_set() : _Mycont(), _Mycomp() {} + // TRANSITION, "_Mycomp" may need to be copied, even in move construction / assignment. template <_Allocator_for _Alloc> - _Base_flat_set(const _Deriv& _Set, const _Alloc& _Al) - : _My_pair(_One_then_variadic_args_t{}, _Set._Get_comp(), _Set._Get_cont(), _Al) {} + _Base_flat_set(const _Deriv& _Set, const _Alloc& _Al) : _Mycont(_Set._Mycont, _Al), _Mycomp(_Set._Mycomp) {} template <_Allocator_for _Alloc> _Base_flat_set(_Deriv&& _Set, const _Alloc& _Al) - : _My_pair(_One_then_variadic_args_t{}, _STD move(_Set._Get_comp()), _STD move(_Set._Get_cont()), _Al) {} + : _Mycont(_STD move(_Set._Mycont), _Al), _Mycomp(_STD move(_Set._Mycomp)) {} explicit _Base_flat_set(container_type _Cont, const key_compare& _Comp = key_compare()) - : _My_pair(_One_then_variadic_args_t{}, _Comp, _STD move(_Cont)) { + : _Mycont(_STD move(_Cont)), _Mycomp(_Comp) { _Make_invariants_fulfilled(); } template <_Allocator_for _Alloc> @@ -90,8 +85,8 @@ public: : _Base_flat_set(container_type(_Cont, _Al), _Comp) {} _Base_flat_set(_Tsorted, container_type _Cont, const key_compare& _Comp = key_compare()) - : _My_pair(_One_then_variadic_args_t{}, _Comp, _STD move(_Cont)) { - _STL_ASSERT(_Check_sorted(cbegin(), cend()), _Msg_not_sorted); + : _Mycont(_STD move(_Cont)), _Mycomp(_Comp) { + _STL_ASSERT(_Is_sorted(_Mycont), _Msg_not_sorted); } template <_Allocator_for _Alloc> _Base_flat_set(_Tsorted _Tsort, const container_type& _Cont, const _Alloc& _Al) @@ -100,12 +95,13 @@ public: _Base_flat_set(_Tsorted _Tsort, const container_type& _Cont, const key_compare& _Comp, const _Alloc& _Al) : _Base_flat_set(_Tsort, container_type(_Cont, _Al), _Comp) {} - explicit _Base_flat_set(const key_compare& _Comp) : _My_pair(_One_then_variadic_args_t{}, _Comp) {} + explicit _Base_flat_set(const key_compare& _Comp) : _Mycont(), _Mycomp(_Comp) {} template <_Allocator_for _Alloc> - _Base_flat_set(const key_compare& _Comp, const _Alloc& _Al) : _My_pair(_One_then_variadic_args_t{}, _Comp, _Al) {} + _Base_flat_set(const key_compare& _Comp, const _Alloc& _Al) : _Mycont(_Al), _Mycomp(_Comp) {} template <_Allocator_for _Alloc> - explicit _Base_flat_set(const _Alloc& _Al) : _My_pair(_Zero_then_variadic_args_t{}, _Al) {} + explicit _Base_flat_set(const _Alloc& _Al) : _Mycont(_Al), _Mycomp() {} + // TRANSITION, an allocator-aware container may not support "C(_First, _Last, _Al)". template _Base_flat_set(_Iter _First, _Iter _Last, const key_compare& _Comp = key_compare()) : _Base_flat_set(container_type(_First, _Last), _Comp) {} @@ -115,6 +111,7 @@ public: template _Alloc> _Base_flat_set(_Iter _First, _Iter _Last, const _Alloc& _Al) : _Base_flat_set(container_type(_First, _Last, _Al)) {} + // TRANSITION, an allocator-aware container may not support "C(from_range, _STD forward<_Rng>(_Range), _Al)". template <_Container_compatible_range<_Kty> _Rng> _Base_flat_set(from_range_t, _Rng&& _Range) : _Base_flat_set(container_type(from_range, _STD forward<_Rng>(_Range))) {} @@ -138,27 +135,27 @@ public: _Base_flat_set(_Tsorted _Tsort, _Iter _First, _Iter _Last, const _Alloc& _Al) : _Base_flat_set(_Tsort, container_type(_First, _Last, _Al)) {} + // TRANSITION, an allocator-aware container may not support "C(_Ilist, _Al)". _Base_flat_set(initializer_list<_Kty> _Ilist, const key_compare& _Comp = key_compare()) - : _Base_flat_set(container_type(_Ilist.begin(), _Ilist.end()), _Comp) {} + : _Base_flat_set(container_type(_Ilist), _Comp) {} template <_Allocator_for _Alloc> _Base_flat_set(initializer_list<_Kty> _Ilist, const key_compare& _Comp, const _Alloc& _Al) - : _Base_flat_set(container_type(_Ilist.begin(), _Ilist.end(), _Al), _Comp) {} + : _Base_flat_set(container_type(_Ilist, _Al), _Comp) {} template <_Allocator_for _Alloc> - _Base_flat_set(initializer_list<_Kty> _Ilist, const _Alloc& _Al) - : _Base_flat_set(container_type(_Ilist.begin(), _Ilist.end(), _Al)) {} + _Base_flat_set(initializer_list<_Kty> _Ilist, const _Alloc& _Al) : _Base_flat_set(container_type(_Ilist, _Al)) {} _Base_flat_set(_Tsorted _Tsort, initializer_list<_Kty> _Ilist, const key_compare& _Comp = key_compare()) - : _Base_flat_set(_Tsort, container_type(_Ilist.begin(), _Ilist.end()), _Comp) {} + : _Base_flat_set(_Tsort, container_type(_Ilist), _Comp) {} template <_Allocator_for _Alloc> _Base_flat_set(_Tsorted _Tsort, initializer_list<_Kty> _Ilist, const key_compare& _Comp, const _Alloc& _Al) - : _Base_flat_set(_Tsort, container_type(_Ilist.begin(), _Ilist.end(), _Al), _Comp) {} + : _Base_flat_set(_Tsort, container_type(_Ilist, _Al), _Comp) {} template <_Allocator_for _Alloc> _Base_flat_set(_Tsorted _Tsort, initializer_list<_Kty> _Ilist, const _Alloc& _Al) - : _Base_flat_set(_Tsort, container_type(_Ilist.begin(), _Ilist.end(), _Al)) {} + : _Base_flat_set(_Tsort, container_type(_Ilist, _Al)) {} _Deriv& operator=(initializer_list<_Kty> _Ilist) { _Clear_guard<_Base_flat_set> _Guard{this}; - _Get_cont().assign(_Ilist.begin(), _Ilist.end()); + _Mycont.assign(_Ilist); _Make_invariants_fulfilled(); _Guard._Target = nullptr; return static_cast<_Deriv&>(*this); @@ -166,16 +163,16 @@ public: // iterators _NODISCARD iterator begin() noexcept { - return _Get_cont().begin(); + return _Mycont.begin(); } _NODISCARD const_iterator begin() const noexcept { - return _Get_cont().begin(); + return _Mycont.begin(); } _NODISCARD iterator end() noexcept { - return _Get_cont().end(); + return _Mycont.end(); } _NODISCARD const_iterator end() const noexcept { - return _Get_cont().end(); + return _Mycont.end(); } _NODISCARD reverse_iterator rbegin() noexcept { @@ -192,10 +189,10 @@ public: } _NODISCARD const_iterator cbegin() const noexcept { - return begin(); + return _Mycont.cbegin(); } _NODISCARD const_iterator cend() const noexcept { - return end(); + return _Mycont.cend(); } _NODISCARD const_reverse_iterator crbegin() const noexcept { return rbegin(); @@ -206,16 +203,18 @@ public: // capacity _NODISCARD_EMPTY_MEMBER bool empty() const noexcept { - return _Get_cont().empty(); + return _Mycont.empty(); } _NODISCARD size_type size() const noexcept { - return _Get_cont().size(); + return _Mycont.size(); } _NODISCARD size_type max_size() const noexcept { - return _Get_cont().max_size(); + return _Mycont.max_size(); } // modifiers + // TRANSITION, the "insert" and "erase" methods may not be able to restore the invariant, if the underlying + // container is unable to provide strong guarantee for "erase" and "insert" methods. template auto emplace(_Args&&... _Vals) { constexpr bool _Is_key_type = _In_place_key_extract_set<_Kty, remove_cvref_t<_Args>...>::_Extractable; @@ -271,13 +270,10 @@ public: void insert_range(_Rng&& _Range) { const size_type _Old_size = size(); - _Container& _Cont = _Get_cont(); - if constexpr (requires { _Cont.append_range(_STD forward<_Rng>(_Range)); }) { - _Cont.append_range(_STD forward<_Rng>(_Range)); + if constexpr (requires { _Mycont.append_range(_STD forward<_Rng>(_Range)); }) { + _Mycont.append_range(_STD forward<_Rng>(_Range)); } else { - for (const auto& _Val : _Range) { - _Cont.insert(_Cont.end(), _Val); - } + _Mycont.insert_range(_Mycont.end(), _STD forward<_Rng>(_Range)); } _Restore_invariants_after_insert(_Old_size); } @@ -289,23 +285,24 @@ public: _Insert_range(_Ilist.begin(), _Ilist.end()); } - _NODISCARD container_type extract() && noexcept(is_nothrow_move_constructible_v<_Container>) /* strengthened */ { + _NODISCARD container_type extract() && noexcept( + is_nothrow_move_constructible_v) /* strengthened */ { // always clears the container (N4950 [flat.set.modifiers]/14 and [flat.multiset.modifiers]/10) - _Clear_guard<_Base_flat_set, is_nothrow_move_constructible_v<_Container>> _Guard{this}; - return _STD move(_Get_cont()); + _Clear_guard<_Base_flat_set> _Guard{this}; + return _STD move(_Mycont); } void replace(container_type&& _Cont) { - _STL_ASSERT(_Check_sorted(_Cont.cbegin(), _Cont.cend()), _Msg_not_sorted); - _Clear_guard<_Base_flat_set, is_nothrow_move_assignable_v<_Container>> _Guard{this}; - _Get_cont() = _STD move(_Cont); + _STL_ASSERT(_Is_sorted(_Cont), _Msg_not_sorted); + _Clear_guard<_Base_flat_set> _Guard{this}; + _Mycont = _STD move(_Cont); _Guard._Target = nullptr; } iterator erase(iterator _Where) { - return _Get_cont().erase(_Where); + return _Mycont.erase(_Where); } iterator erase(const_iterator _Where) { - return _Get_cont().erase(_Where); + return _Mycont.erase(_Where); } size_type erase(const _Kty& _Val) { return _Erase(_Val); @@ -317,23 +314,23 @@ public: return _Erase(_Val); } iterator erase(const_iterator _First, const_iterator _Last) { - return _Get_cont().erase(_First, _Last); + return _Mycont.erase(_First, _Last); } void swap(_Deriv& _Other) noexcept { - _RANGES swap(_Get_comp(), _Other._Get_comp()); - _RANGES swap(_Get_cont(), _Other._Get_cont()); + _RANGES swap(_Mycomp, _Other._Mycomp); + _RANGES swap(_Mycont, _Other._Mycont); } void clear() noexcept { - _Get_cont().clear(); + _Mycont.clear(); } // observers _NODISCARD key_compare key_comp() const { - return _Get_comp(); + return _Mycomp; } _NODISCARD value_compare value_comp() const { - return _Get_comp(); + return _Mycomp; } // set operations @@ -371,70 +368,70 @@ public: } _NODISCARD bool contains(const _Kty& _Val) const { - return _STD binary_search(cbegin(), cend(), _Val, _Get_comp_v()); + return _STD binary_search(cbegin(), cend(), _Val, _Pass_comp()); } template requires _Keylt_transparent _NODISCARD bool contains(const _Other& _Val) const { - return _STD binary_search(cbegin(), cend(), _Val, _Get_comp_v()); + return _STD binary_search(cbegin(), cend(), _Val, _Pass_comp()); } _NODISCARD iterator lower_bound(const _Kty& _Val) { - return _STD lower_bound(begin(), end(), _Val, _Get_comp_v()); + return _STD lower_bound(begin(), end(), _Val, _Pass_comp()); } _NODISCARD const_iterator lower_bound(const _Kty& _Val) const { - return _STD lower_bound(cbegin(), cend(), _Val, _Get_comp_v()); + return _STD lower_bound(cbegin(), cend(), _Val, _Pass_comp()); } template requires _Keylt_transparent _NODISCARD iterator lower_bound(const _Other& _Val) { - return _STD lower_bound(begin(), end(), _Val, _Get_comp_v()); + return _STD lower_bound(begin(), end(), _Val, _Pass_comp()); } template requires _Keylt_transparent _NODISCARD const_iterator lower_bound(const _Other& _Val) const { - return _STD lower_bound(cbegin(), cend(), _Val, _Get_comp_v()); + return _STD lower_bound(cbegin(), cend(), _Val, _Pass_comp()); } _NODISCARD iterator upper_bound(const _Kty& _Val) { - return _STD upper_bound(begin(), end(), _Val, _Get_comp_v()); + return _STD upper_bound(begin(), end(), _Val, _Pass_comp()); } _NODISCARD const_iterator upper_bound(const _Kty& _Val) const { - return _STD upper_bound(cbegin(), cend(), _Val, _Get_comp_v()); + return _STD upper_bound(cbegin(), cend(), _Val, _Pass_comp()); } template requires _Keylt_transparent _NODISCARD iterator upper_bound(const _Other& _Val) { - return _STD upper_bound(begin(), end(), _Val, _Get_comp_v()); + return _STD upper_bound(begin(), end(), _Val, _Pass_comp()); } template requires _Keylt_transparent _NODISCARD const_iterator upper_bound(const _Other& _Val) const { - return _STD upper_bound(cbegin(), cend(), _Val, _Get_comp_v()); + return _STD upper_bound(cbegin(), cend(), _Val, _Pass_comp()); } _NODISCARD pair equal_range(const _Kty& _Val) { - return _STD equal_range(begin(), end(), _Val, _Get_comp_v()); + return _STD equal_range(begin(), end(), _Val, _Pass_comp()); } _NODISCARD pair equal_range(const _Kty& _Val) const { - return _STD equal_range(cbegin(), cend(), _Val, _Get_comp_v()); + return _STD equal_range(cbegin(), cend(), _Val, _Pass_comp()); } template requires _Keylt_transparent _NODISCARD pair equal_range(const _Other& _Val) { - return _STD equal_range(begin(), end(), _Val, _Get_comp_v()); + return _STD equal_range(begin(), end(), _Val, _Pass_comp()); } template requires _Keylt_transparent _NODISCARD pair equal_range(const _Other& _Val) const { - return _STD equal_range(cbegin(), cend(), _Val, _Get_comp_v()); + return _STD equal_range(cbegin(), cend(), _Val, _Pass_comp()); } _NODISCARD friend bool operator==(const _Deriv& _Lhs, const _Deriv& _Rhs) { - return _RANGES equal(_Lhs._Get_cont(), _Rhs._Get_cont()); + return _RANGES equal(_Lhs._Mycont, _Rhs._Mycont); } _NODISCARD friend auto operator<=>(const _Deriv& _Lhs, const _Deriv& _Rhs) { @@ -447,9 +444,9 @@ public: } private: - _NODISCARD bool _Check_sorted(const_iterator _It, const const_iterator _End) const { + _NODISCARD bool _Is_sorted(const_iterator _It, const const_iterator _End) const { if constexpr (_Multi) { - return _STD is_sorted(_It, _End, _Get_comp_v()); + return _STD is_sorted(_It, _End, _Pass_comp()); } else { // sorted-unique if (_It == _End) { @@ -464,9 +461,16 @@ private: } } + _NODISCARD bool _Is_sorted(const container_type& _Cont) const { + return _Is_sorted(_Cont.cbegin(), _Cont.cend()); + } + static constexpr const char* _Msg_not_sorted = _Multi ? "Input was not sorted!" : "Input was not sorted-unique!"; - _NODISCARD bool _Check_where(const const_iterator _Where, const _Kty& _Val) const { + template + _NODISCARD bool _Can_insert(const const_iterator _Where, const _Ty& _Val) const { + _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Ty, _Kty>); // only accepts _Kty + // check that _Val can be inserted before _Where if constexpr (_Multi) { // check that _Where is the upper_bound for _Val @@ -482,94 +486,102 @@ private: } template - auto _Emplace(_Ty&& _Val) { - _Container& _Cont = _Get_cont(); - if constexpr (_Multi) { - _STL_INTERNAL_STATIC_ASSERT(is_same_v, _Kty>); - return _Cont.emplace(upper_bound(_Val), _STD forward<_Ty>(_Val)); + requires _Multi // flat_multiset + iterator _Emplace(_Ty&& _Val) { + _STL_INTERNAL_STATIC_ASSERT(is_same_v, _Kty>); + return _Mycont.emplace(upper_bound(_Val), _STD forward<_Ty>(_Val)); + } + + template + requires (!_Multi) // flat_set + pair _Emplace(_Ty&& _Val) { + const iterator _Where = lower_bound(_Val); + if (_Where != end() && !_Compare(_Val, *_Where)) { + return pair{_Where, false}; + } + + if constexpr (is_same_v, _Kty>) { + return pair{_Mycont.emplace(_Where, _STD forward<_Ty>(_Val)), true}; } else { - const iterator _End = end(); - const iterator _Where = lower_bound(_Val); - if (_Where != _End && !_Compare(_Val, *_Where)) { - return pair{_Where, false}; - } + // heterogeneous insertion + // TRANSITION, the requirements may be stricter than needed by the standard. + _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent && is_constructible_v<_Kty, _Ty>); + _Kty _Keyval(_STD forward<_Ty>(_Val)); + _STL_ASSERT(_Can_insert(_Where, _Keyval), "The conversion from the heterogeneous key to key_type should " + "be consistent with the heterogeneous lookup!"); + return pair{_Mycont.emplace(_Where, _STD move(_Keyval)), true}; + } + } + + template + requires _Multi // flat_multiset + iterator _Emplace_hint(const_iterator _Where, _Ty&& _Val) { + _STL_INTERNAL_STATIC_ASSERT(is_same_v, _Kty>); - if constexpr (is_same_v, _Kty>) { - return pair{_Cont.emplace(_Where, _STD forward<_Ty>(_Val)), true}; + const const_iterator _Begin = cbegin(); + const const_iterator _End = cend(); + + // look for upper_bound(_Val) + if (_Where == _End || _Compare(_Val, *_Where)) { + // _Val < *_Where + if (_Where == _Begin || !_Compare(_Val, *(_Where - 1))) { + // _Val >= *(_Where-1) ~ upper_bound is _Where } else { - // for flat_set::insert(auto&&) - _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent && is_constructible_v<_Kty, _Ty>); - _Kty _Keyval(_STD forward<_Ty>(_Val)); - _STL_ASSERT(_Check_where(_Where, _Keyval), "The input type was not equivalent to key_type!"); - return pair{_Cont.emplace(_Where, _STD move(_Keyval)), true}; + // _Val < *(_Where-1) ~ upper_bound is in [_Begin,_Where-1] + _Where = _STD upper_bound(_Begin, _Where - 1, _Val, _Pass_comp()); } + } else { + // _Val >= *_Where ~ upper_bound is in [_Where+1,_End] + _Where = _STD upper_bound(_Where + 1, _End, _Val, _Pass_comp()); } + + _STL_INTERNAL_CHECK(_Can_insert(_Where, _Val)); + return _Mycont.emplace(_Where, _STD forward<_Ty>(_Val)); } template + requires (!_Multi) // flat_set iterator _Emplace_hint(const_iterator _Where, _Ty&& _Val) { - _Container& _Cont = _Get_cont(); - auto _Comp = _Get_comp_v(); const const_iterator _Begin = cbegin(); const const_iterator _End = cend(); - if constexpr (_Multi) { - // look for the upper_bound for flat_multiset - if (_Where == _End || _Compare(_Val, *_Where)) { - // _Val < *_Where - if (_Where == _Begin || !_Compare(_Val, *(_Where - 1))) { - // _Val >= *(_Where-1) ~ upper_bound is _Where - } else { - // _Val < *(_Where-1) ~ upper_bound is in [_Begin,_Where-1] - _Where = _STD upper_bound(_Begin, _Where - 1, _Val, _Comp); - } + // look for lower_bound(_Val) + if (_Where == _End || !_Compare(*_Where, _Val)) { + // _Val <= *_Where + if (_Where == _Begin || _Compare(*(_Where - 1), _Val)) { + // _Val > *(_Where-1) ~ lower_bound is _Where } else { - // _Val >= *_Where ~ upper_bound is in [_Where+1,_End] - _Where = _STD upper_bound(_Where + 1, _End, _Val, _Comp); + // _Val <= *(_Where-1) ~ lower_bound is in [_Begin,_Where-1] + _Where = _STD lower_bound(_Begin, _Where - 1, _Val, _Pass_comp()); } } else { - // look for the lower_bound for flat_set - if (_Where == _End || !_Compare(*_Where, _Val)) { - // _Val <= *_Where - if (_Where == _Begin || _Compare(*(_Where - 1), _Val)) { - // _Val > *(_Where-1) ~ lower_bound is _Where - } else { - // _Val <= *(_Where-1) ~ lower_bound is in [_Begin,_Where-1] - _Where = _STD lower_bound(_Begin, _Where - 1, _Val, _Comp); - } - } else { - // _Val > *_Where ~ lower_bound is in [_Where+1,_End] - _Where = _STD lower_bound(_Where + 1, _End, _Val, _Comp); - } + // _Val > *_Where ~ lower_bound is in [_Where+1,_End] + _Where = _STD lower_bound(_Where + 1, _End, _Val, _Pass_comp()); } - if constexpr (_Multi) { - _STL_INTERNAL_STATIC_ASSERT(is_same_v, _Kty>); - _STL_INTERNAL_CHECK(_Check_where(_Where, _Val)); - return _Cont.emplace(_Where, _STD forward<_Ty>(_Val)); - } else { - if (_Where != _End && !_Compare(_Val, *_Where)) { - return _Cont.begin() + (_Where - _Begin); - } + if (_Where != _End && !_Compare(_Val, *_Where)) { + // convert const_iterator to iterator + return _Mycont.begin() + (_Where - _Begin); + } - if constexpr (is_same_v, _Kty>) { - _STL_INTERNAL_CHECK(_Check_where(_Where, _Val)); - return _Cont.emplace(_Where, _STD forward<_Ty>(_Val)); - } else { - // for flat_set::insert(hint,auto&&) - _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent && is_constructible_v<_Kty, _Ty>); - _Kty _Keyval(_STD forward<_Ty>(_Val)); - _STL_ASSERT(_Check_where(_Where, _Keyval), "The input type was not equivalent to key_type!"); - return _Cont.emplace(_Where, _STD move(_Keyval)); - } + if constexpr (is_same_v, _Kty>) { + _STL_INTERNAL_CHECK(_Can_insert(_Where, _Val)); + return _Mycont.emplace(_Where, _STD forward<_Ty>(_Val)); + } else { + // heterogeneous insertion + // TRANSITION, the requirements may be stricter than needed by the standard. + _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent && is_constructible_v<_Kty, _Ty>); + _Kty _Keyval(_STD forward<_Ty>(_Val)); + _STL_ASSERT(_Can_insert(_Where, _Keyval), "The conversion from the heterogeneous key to key_type should " + "be consistent with the heterogeneous lookup!"); + return _Mycont.emplace(_Where, _STD move(_Keyval)); } } template void _Insert_range(const _Iter _First, const _Iter _Last) { const size_type _Old_size = size(); - _Container& _Cont = _Get_cont(); - _Cont.insert(_Cont.end(), _First, _Last); + _Mycont.insert(_Mycont.end(), _First, _Last); _Restore_invariants_after_insert<_Presorted>(_Old_size); } @@ -580,7 +592,7 @@ private: if constexpr (!_Multi && is_same_v<_Ty, _Kty>) { const iterator _Where = lower_bound(_Val); if (_Where != end() && !_Compare(_Val, *_Where)) { - _Get_cont().erase(_Where); + _Mycont.erase(_Where); return 1; } return 0; @@ -588,7 +600,7 @@ private: const auto [_First, _Last] = equal_range(_Val); const auto _Removed = static_cast(_Last - _First); - _Get_cont().erase(_First, _Last); + _Mycont.erase(_First, _Last); return _Removed; } } @@ -621,32 +633,30 @@ private: void _Erase_dupes_if_not_multi() { if constexpr (!_Multi) { - const auto _Equal_to = [this](const _Kty& _Lhs, const _Kty& _Rhs) { + const auto _Equivalent = [this](const _Kty& _Lhs, const _Kty& _Rhs) { return !_Compare(_Lhs, _Rhs) && !_Compare(_Rhs, _Lhs); }; - const iterator _End = end(); - const iterator _New_end = _STD unique(begin(), _End, _Equal_to); - _Get_cont().erase(_New_end, _End); + const iterator _End = end(); + _Mycont.erase(_STD unique(begin(), _End, _Equivalent), _End); } } template void _Restore_invariants_after_insert(const size_type _Old_size) { - auto _Comp = _Get_comp_v(); const iterator _Begin = begin(); const iterator _Old_end = _Begin + static_cast(_Old_size); - const iterator _New_end = end(); + const iterator _End = end(); if constexpr (!_Presorted) { - _STD sort(_Old_end, _New_end, _Comp); + _STD sort(_Old_end, _End, _Pass_comp()); } else { - _STL_ASSERT(_Check_sorted(_Old_end, _New_end), _Msg_not_sorted); + _STL_ASSERT(_Is_sorted(_Old_end, _End), _Msg_not_sorted); } - _STD inplace_merge(_Begin, _Old_end, _New_end, _Comp); + _STD inplace_merge(_Begin, _Old_end, _End, _Pass_comp()); _Erase_dupes_if_not_multi(); - _STL_INTERNAL_CHECK(_Check_sorted(cbegin(), cend())); + _STL_INTERNAL_CHECK(_Is_sorted(_Mycont)); } void _Make_invariants_fulfilled() { @@ -658,45 +668,29 @@ private: } // O(N) if already sorted. - auto _Comp = _Get_comp_v(); - const iterator _Begin_unsorted = _STD is_sorted_until(_Begin, _End, _Comp); + const iterator _Begin_unsorted = _STD is_sorted_until(_Begin, _End, _Pass_comp()); - _STD sort(_Begin_unsorted, _End, _Comp); - _STD inplace_merge(_Begin, _Begin_unsorted, _End, _Comp); + _STD sort(_Begin_unsorted, _End, _Pass_comp()); + _STD inplace_merge(_Begin, _Begin_unsorted, _End, _Pass_comp()); _Erase_dupes_if_not_multi(); - _STL_INTERNAL_CHECK(_Check_sorted(cbegin(), cend())); + _STL_INTERNAL_CHECK(_Is_sorted(_Mycont)); } template _NODISCARD bool _Compare(const _Lty& _Lhs, const _Rty& _Rhs) const - noexcept(noexcept(_DEBUG_LT_PRED(_My_pair._Get_first(), _Lhs, _Rhs))) { + noexcept(noexcept(_DEBUG_LT_PRED(_Mycomp, _Lhs, _Rhs))) { _STL_INTERNAL_STATIC_ASSERT(_Keylt_transparent || (is_same_v<_Kty, _Lty> && is_same_v<_Kty, _Rty>) ); - return _DEBUG_LT_PRED(_My_pair._Get_first(), _Lhs, _Rhs); - } - - _NODISCARD const _Container& _Get_cont() const noexcept { - return _My_pair._Myval2; - } - - _NODISCARD _Container& _Get_cont() noexcept { - return _My_pair._Myval2; - } - - _NODISCARD const key_compare& _Get_comp() const noexcept { - return _My_pair._Get_first(); - } - - _NODISCARD key_compare& _Get_comp() noexcept { - return _My_pair._Get_first(); + return _DEBUG_LT_PRED(_Mycomp, _Lhs, _Rhs); } - _NODISCARD auto _Get_comp_v() const noexcept { - return _STD _Pass_fn(_My_pair._Get_first()); + _NODISCARD auto _Pass_comp() const noexcept { + return _STD _Pass_fn(_Mycomp); } - _Compressed_pair _My_pair; + container_type _Mycont; + /* [[no_unique_address]] */ key_compare _Mycomp; }; _EXPORT_STD struct sorted_unique_t { diff --git a/tests/std/tests/P1222R4_flat_set/test.cpp b/tests/std/tests/P1222R4_flat_set/test.cpp index 9bb2adcd0df..e0dad813302 100644 --- a/tests/std/tests/P1222R4_flat_set/test.cpp +++ b/tests/std/tests/P1222R4_flat_set/test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include using namespace std; @@ -67,23 +68,10 @@ void assert_reversible_container_requirements(const T& s) { static_assert(is_convertible_v); } -template -void test_ebco() { - // This tests an implementation-specific optimization. - using key_compare = T::key_compare; - using container_type = T::container_type; - if constexpr (is_empty_v && !is_final_v) { - static_assert(sizeof(container_type) == sizeof(T)); - } else { - static_assert(sizeof(container_type) < sizeof(T)); - } -} - template void assert_all_requirements_and_equals(const T& s, const initializer_list& il) { assert_container_requirements(s); assert_reversible_container_requirements(s); - test_ebco(); auto val_comp = s.value_comp(); auto begin_it = s.cbegin(); @@ -408,14 +396,75 @@ void test_non_static_comparer() { assert_all_requirements_and_equals(a, {9, 7, 5, -1}); } +template