-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[libc++] Move out flat_map::iterator
(for reusing it in flat_multimap)
#117445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
// -*- 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_MAP_KEY_VALUE_ITERATOR_H | ||
#define _LIBCPP___FLAT_MAP_KEY_VALUE_ITERATOR_H | ||
|
||
#include <__compare/three_way_comparable.h> | ||
#include <__concepts/convertible_to.h> | ||
#include <__config> | ||
#include <__iterator/iterator_traits.h> | ||
#include <__memory/addressof.h> | ||
#include <__ranges/access.h> | ||
#include <__type_traits/conditional.h> | ||
#include <__type_traits/maybe_const.h> | ||
#include <__utility/move.h> | ||
#include <__utility/pair.h> | ||
|
||
#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 | ||
|
||
/** | ||
* __key_value_iterator is a proxy iterator which zips the underlying | ||
* _KeyContainer::iterator and the underlying _MappedContainer::iterator. | ||
* The two underlying iterators will be incremented/decremented together. | ||
* And the reference is a pair of the const key reference and the value reference. | ||
*/ | ||
template <class _Owner, class _KeyContainer, class _MappedContainer, bool _Const> | ||
struct __key_value_iterator { | ||
huixie90 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
private: | ||
using __key_iterator = ranges::iterator_t<const _KeyContainer>; | ||
using __mapped_iterator = ranges::iterator_t<__maybe_const<_Const, _MappedContainer>>; | ||
using __reference = _If<_Const, typename _Owner::const_reference, typename _Owner::reference>; | ||
|
||
struct __arrow_proxy { | ||
__reference __ref_; | ||
_LIBCPP_HIDE_FROM_ABI __reference* operator->() { return std::addressof(__ref_); } | ||
}; | ||
|
||
__key_iterator __key_iter_; | ||
__mapped_iterator __mapped_iter_; | ||
|
||
friend _Owner; | ||
|
||
template <class, class, class, bool> | ||
friend struct __key_value_iterator; | ||
|
||
public: | ||
using iterator_concept = random_access_iterator_tag; | ||
// `__key_value_iterator` only satisfy "Cpp17InputIterator" named requirements, because | ||
// its `reference` is not a reference type. | ||
// However, to avoid surprising runtime behaviour when it is used with the | ||
// Cpp17 algorithms or operations, iterator_category is set to random_access_iterator_tag. | ||
using iterator_category = random_access_iterator_tag; | ||
using value_type = typename _Owner::value_type; | ||
using difference_type = typename _Owner::difference_type; | ||
|
||
_LIBCPP_HIDE_FROM_ABI __key_value_iterator() = default; | ||
|
||
_LIBCPP_HIDE_FROM_ABI __key_value_iterator(__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, !_Const> __i) | ||
requires _Const && convertible_to<ranges::iterator_t<_KeyContainer>, __key_iterator> && | ||
convertible_to<ranges::iterator_t<_MappedContainer>, __mapped_iterator> | ||
: __key_iter_(std::move(__i.__key_iter_)), __mapped_iter_(std::move(__i.__mapped_iter_)) {} | ||
|
||
_LIBCPP_HIDE_FROM_ABI __key_value_iterator(__key_iterator __key_iter, __mapped_iterator __mapped_iter) | ||
: __key_iter_(std::move(__key_iter)), __mapped_iter_(std::move(__mapped_iter)) {} | ||
|
||
_LIBCPP_HIDE_FROM_ABI __reference operator*() const { return __reference(*__key_iter_, *__mapped_iter_); } | ||
_LIBCPP_HIDE_FROM_ABI __arrow_proxy operator->() const { return __arrow_proxy{**this}; } | ||
|
||
_LIBCPP_HIDE_FROM_ABI __key_value_iterator& operator++() { | ||
++__key_iter_; | ||
++__mapped_iter_; | ||
return *this; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI __key_value_iterator operator++(int) { | ||
__key_value_iterator __tmp(*this); | ||
++*this; | ||
return __tmp; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI __key_value_iterator& operator--() { | ||
--__key_iter_; | ||
--__mapped_iter_; | ||
return *this; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI __key_value_iterator operator--(int) { | ||
__key_value_iterator __tmp(*this); | ||
--*this; | ||
return __tmp; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI __key_value_iterator& operator+=(difference_type __x) { | ||
__key_iter_ += __x; | ||
__mapped_iter_ += __x; | ||
return *this; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI __key_value_iterator& operator-=(difference_type __x) { | ||
__key_iter_ -= __x; | ||
__mapped_iter_ -= __x; | ||
return *this; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI __reference operator[](difference_type __n) const { return *(*this + __n); } | ||
|
||
_LIBCPP_HIDE_FROM_ABI friend constexpr bool | ||
operator==(const __key_value_iterator& __x, const __key_value_iterator& __y) { | ||
return __x.__key_iter_ == __y.__key_iter_; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI friend bool operator<(const __key_value_iterator& __x, const __key_value_iterator& __y) { | ||
return __x.__key_iter_ < __y.__key_iter_; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI friend bool operator>(const __key_value_iterator& __x, const __key_value_iterator& __y) { | ||
return __y < __x; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI friend bool operator<=(const __key_value_iterator& __x, const __key_value_iterator& __y) { | ||
return !(__y < __x); | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI friend bool operator>=(const __key_value_iterator& __x, const __key_value_iterator& __y) { | ||
return !(__x < __y); | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI friend auto operator<=>(const __key_value_iterator& __x, const __key_value_iterator& __y) | ||
requires three_way_comparable<__key_iterator> | ||
{ | ||
return __x.__key_iter_ <=> __y.__key_iter_; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI friend __key_value_iterator operator+(const __key_value_iterator& __i, difference_type __n) { | ||
auto __tmp = __i; | ||
__tmp += __n; | ||
return __tmp; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI friend __key_value_iterator operator+(difference_type __n, const __key_value_iterator& __i) { | ||
return __i + __n; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI friend __key_value_iterator operator-(const __key_value_iterator& __i, difference_type __n) { | ||
auto __tmp = __i; | ||
__tmp -= __n; | ||
return __tmp; | ||
} | ||
|
||
_LIBCPP_HIDE_FROM_ABI friend difference_type | ||
operator-(const __key_value_iterator& __x, const __key_value_iterator& __y) { | ||
return difference_type(__x.__key_iter_ - __y.__key_iter_); | ||
} | ||
}; | ||
|
||
_LIBCPP_END_NAMESPACE_STD | ||
|
||
#endif // _LIBCPP_STD_VER >= 23 | ||
|
||
_LIBCPP_POP_MACROS | ||
|
||
#endif // _LIBCPP___FLAT_MAP_KEY_VALUE_ITERATOR_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ | |
#include <flat_map> | ||
#include <random> | ||
#include <map> | ||
#include <vector> | ||
|
||
#include "test_macros.h" | ||
|
||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
// type. | ||
|
||
#include <flat_map> | ||
#include <vector> | ||
|
||
struct A { | ||
using Map = std::flat_map<A, A>; | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it intended that two different
flat_(multi)map
specializations never have the same iterator types (i.e. the design is completely non-SCARY)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a requirement to provide SCARY iterators? I think there were some types that were required to provide SCARY iterators since C++11, but my memory is failing me. I'm not certain if there is some library-wide requirement about this?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. N2980 proposed requiring SCARY, but it was effectively rejected (per LWG1242). Currently SCARY-ness is totally part of quality of implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
had discussion with @ldionne, we definitely don't want to allow
flat_map
andflat_multimap
's iterator to be interchangeable. Supporting SCARY iterators would allow erroneous programs which can be easily caught at compile time otherwise.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ Or at least, that's our current thinking. We're open to being persuaded otherwise but our thinking was that if a user has the bad luck of unintendedly mixing up
flat_map::iterator
withflat_multimap::iterator
, they'd rather know that at compile-time. IOW, not providing SCARY iterators seems like a feature more than a QOI deficiency in this situation. As I said, that's just our current thinking and it can evolve.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ldionne We could be a little bit SCARY where we don' allow the mix of
flat_map
andflat_multimap
. But we can allow the mix offlat_map<int, int, std::less<int>>::iterator
withflat_map<int, int, std::greater<int>>::iterator
. (The benefit is less number of template instantiations but one could also argue this is a bug rather than a feature). That is what we did formap
https://godbolt.org/z/57zTGoGd3
To achieve this, we could do something like
where
_Tag
can be__flat_map_tag
or__flat_multimap_tag
However, I am not a big fan of SCARY iterators. I think it is a Non-QoI rather than a QoI. The mix of those iterators are extremely likely a bug in the user code where we could easily detect at compile time. SCARY iterators simply allow the erroneous user code which against making C++ a safe language initiative.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In https://devblogs.microsoft.com/cppblog/what-are-scary-iterators/, they make a point that e.g. different allocators should probably not result in different iterators. That makes some sense to me, although I guess it depends what the pointer type is. But here it seems like the amount of stuff we need to cary around in
__key_value_iterator
is probably not worth it.