[libcxx-commits] [PATCH] D142864: [libc++] `<algorithm>`: `ranges::minmax` should dereference iterators only once
Igor Zhukov via Phabricator via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Jan 31 21:14:50 PST 2023
fsb4000 updated this revision to Diff 493819.
fsb4000 added a comment.
Remove an unneeded move.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D142864/new/
https://reviews.llvm.org/D142864
Files:
libcxx/include/__algorithm/ranges_minmax.h
libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.minmax.pass.cpp
Index: libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.minmax.pass.cpp
===================================================================
--- libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.minmax.pass.cpp
+++ libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.minmax.pass.cpp
@@ -334,12 +334,49 @@
return true;
}
+class input_move_iterator {
+public:
+ using iterator_category = input_iterator_tag;
+ using iterator_concept = input_iterator_tag;
+ using difference_type = ptrdiff_t;
+ using value_type = shared_ptr<int>;
+ using pointer = shared_ptr<int>*;
+ using reference = shared_ptr<int>&&;
+
+ input_move_iterator() = default;
+ explicit input_move_iterator(shared_ptr<int>* ptr) : m_ptr(ptr) {}
+
+ reference operator*() const {
+ return ranges::iter_move(m_ptr);
+ }
+ pointer operator->() const {
+ return m_ptr;
+ }
+
+ input_move_iterator& operator++() {
+ ++m_ptr;
+ return *this;
+ }
+ input_move_iterator operator++(int) {
+ input_move_iterator tmp = *this;
+ ++*this;
+ return tmp;
+ }
+
+ friend bool operator==(const input_move_iterator& a, const input_move_iterator& b) {
+ return a.m_ptr == b.m_ptr;
+ }
+
+private:
+ shared_ptr<int>* m_ptr{nullptr};
+};
+
int main(int, char**) {
test();
static_assert(test());
{
- // check that the iterator isn't moved from multiple times
+ // check that the random access iterator isn't moved from multiple times
std::shared_ptr<int> a[] = { std::make_shared<int>(42) };
auto range = std::ranges::subrange(std::move_iterator(a), std::move_iterator(a + 1));
auto [min, max] = std::ranges::minmax(range);
@@ -347,6 +384,15 @@
assert(min != nullptr);
assert(max == min);
}
+ {
+ // check that the input iterator isn't moved from multiple times
+ std::shared_ptr<int> a[] = { std::make_shared<int>(42) };
+ auto range = std::ranges::subrange(input_move_iterator(a), input_move_iterator(a + 1));
+ auto [min, max] = std::ranges::minmax(range);
+ assert(a[0] == nullptr);
+ assert(min != nullptr);
+ assert(max == min);
+ }
return 0;
}
Index: libcxx/include/__algorithm/ranges_minmax.h
===================================================================
--- libcxx/include/__algorithm/ranges_minmax.h
+++ libcxx/include/__algorithm/ranges_minmax.h
@@ -74,6 +74,15 @@
if constexpr (forward_range<_Range>) {
auto __result = std::__minmax_element_impl(__first, __last, __comp, __proj);
+ // We can dereference normal pointers many times but some iterators could move their value after
+ // dereferencing so we should dereference them only once. The `if constexpr` is a pure optimization;
+ // we don't want an additional branch for normal pointers.
+ if constexpr (!is_pointer_v<iterator_t<_Range>>) {
+ if (__result.first == __result.second) {
+ ranges::minmax_result<_ValueT> __found = {*__result.first, __found.min};
+ return __found;
+ }
+ }
return {*__result.first, *__result.second};
} else {
// input_iterators can't be copied, so the implementation for input_iterators has to store
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D142864.493819.patch
Type: text/x-patch
Size: 3208 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/libcxx-commits/attachments/20230201/61ac5c4b/attachment-0001.bin>
More information about the libcxx-commits
mailing list