[libcxx-commits] [libcxx] [libc++] implement std::flat_set (PR #125241)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Feb 21 11:29:15 PST 2025
================
@@ -0,0 +1,853 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FLAT_set_FLAT_SET_H
+#define _LIBCPP___FLAT_set_FLAT_SET_H
+
+#include <__algorithm/lexicographical_compare_three_way.h>
+#include <__algorithm/min.h>
+#include <__algorithm/ranges_adjacent_find.h>
+#include <__algorithm/ranges_equal.h>
+#include <__algorithm/ranges_inplace_merge.h>
+#include <__algorithm/ranges_lower_bound.h>
+#include <__algorithm/ranges_partition_point.h>
+#include <__algorithm/ranges_sort.h>
+#include <__algorithm/ranges_unique.h>
+#include <__algorithm/ranges_upper_bound.h>
+#include <__algorithm/remove_if.h>
+#include <__assert>
+#include <__compare/synth_three_way.h>
+#include <__concepts/swappable.h>
+#include <__config>
+#include <__cstddef/byte.h>
+#include <__cstddef/ptrdiff_t.h>
+#include <__flat_map/sorted_unique.h>
+#include <__functional/invoke.h>
+#include <__functional/is_transparent.h>
+#include <__functional/operations.h>
+#include <__fwd/vector.h>
+#include <__iterator/concepts.h>
+#include <__iterator/distance.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/next.h>
+#include <__iterator/ranges_iterator_traits.h>
+#include <__iterator/reverse_iterator.h>
+#include <__memory/allocator_traits.h>
+#include <__memory/uses_allocator.h>
+#include <__memory/uses_allocator_construction.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/drop_view.h>
+#include <__ranges/from_range.h>
+#include <__ranges/ref_view.h>
+#include <__ranges/size.h>
+#include <__ranges/subrange.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/container_traits.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_allocator.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_same.h>
+#include <__utility/exception_guard.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <__utility/scope_guard.h>
+#include <__vector/vector.h>
+#include <initializer_list>
+#include <stdexcept>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 23
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Key, class _Compare = less<_Key>, class _KeyContainer = vector<_Key>>
+class flat_set {
+ template <class, class, class>
+ friend class flat_set;
+
+ static_assert(is_same_v<_Key, typename _KeyContainer::value_type>);
+ static_assert(!is_same_v<_KeyContainer, std::vector<bool>>, "vector<bool> is not a sequence container");
+
+public:
+ // types
+ using key_type = _Key;
+ using value_type = _Key;
+ using key_compare = __type_identity_t<_Compare>;
+ using value_compare = _Compare;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using size_type = typename _KeyContainer::size_type;
+ using difference_type = typename _KeyContainer::difference_type;
+ using iterator = typename _KeyContainer::const_iterator;
----------------
ldionne wrote:
Another thought I just had during live review just now is that by using SCARY iterators, we might be throwing away useful semantic information about the underlying structure of the data. For example, if we can tell that we have `flat_set` iterators, we know that the data we're looking at is sorted. It has to be sorted because that's an invariant of the container.
As a result, in e..g `std::find`, we could potentially have an optimization for `std::find(flat_set::iterator, flat_set::iterator)` where we assume sorted data. This seems potentially useful.
So I would try to ensure that we can tell what container the iterator comes from.
We could potentially call it something like `std::__random_access_iterator<Container, wrapped_iterator>` or shorter like `std::__ra_iterator<Container, wrapped_iterator>`. We have kind-of a precedent for this, `__wrapped_iter` is effectively a `std::__contiguous_iterator` wrapper that we use in a bunch of places, except we didn't retain the container type and we didn't name it very well back then.
Here's a thought I just had:
```c++
template <class _Container, class _Iterator>
struct __ra_iterator { /* whatever ... */ };
template <class _Container, class _Iterator>
using __contiguous_iterator =
if (_Container == std::vector<T>) return __wrap_iter<_Iterator>; // for backwards compatibility
else if (_Container == std::string) return __wrap_iter<_Iterator>; // backwards compat, etc..
else /* some actual struct like __ra_iterator but for contiguous */;
```
And then from `std::vector<T>`, we actually do this:
```
using __iterator = __contiguous_iterator<std::vector<T>, T*>;
```
https://github.com/llvm/llvm-project/pull/125241
More information about the libcxx-commits
mailing list