//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 // // constexpr iterator& operator--(); // constexpr iterator operator--(int); #include #include #include #include #include #include #include #include #include "../types.h" #include "test_iterators.h" #include "test_macros.h" template concept HasPreDecrement = requires(T t) { { --t }; }; template concept HasPostDecrement = requires(T t) { { t-- }; }; struct TrackingPred : TrackInitialization { using TrackInitialization::TrackInitialization; constexpr bool operator()(int x, int y) const { return x <= y; } }; template > constexpr void test() { using Underlying = View; using ChunkByView = std::ranges::chunk_by_view; using ChunkByIterator = std::ranges::iterator_t; static_assert(HasPostDecrement); static_assert(HasPreDecrement); auto make_chunk_by_view = [](auto& arr) { View view{Iter{arr.data()}, Sent{Iter{arr.data() + arr.size()}}}; return ChunkByView{std::move(view), std::ranges::less_equal{}}; }; // Test with a single chunk { std::array array{0, 1, 2, 3, 4}; ChunkByView view = make_chunk_by_view(array); ChunkByIterator it = std::ranges::next(view.begin(), view.end()); std::same_as decltype(auto) result = --it; assert(&result == &it); assert(base((*result).begin()) == array.data()); } // Test with two chunks { std::array array{0, 1, 2, 0, 1, 2}; ChunkByView view = make_chunk_by_view(array); ChunkByIterator it = std::ranges::next(view.begin(), view.end()); std::same_as decltype(auto) result = --it; assert(&result == &it); assert(base((*result).begin()) == array.data() + 3); --it; assert(base((*result).begin()) == array.data()); } // Test going forward and then backward on the same iterator { std::array array{7, 8, 9, 4, 5, 6, 1, 2, 3, 0}; ChunkByView view = make_chunk_by_view(array); ChunkByIterator it = view.begin(); ++it; --it; assert(base((*it).begin()) == array.data()); assert(base((*it).end()) == array.data() + 3); ++it; ++it; --it; assert(base((*it).begin()) == array.data() + 3); assert(base((*it).end()) == array.data() + 6); ++it; ++it; --it; assert(base((*it).begin()) == array.data() + 6); assert(base((*it).end()) == array.data() + 9); ++it; ++it; --it; assert(base((*it).begin()) == array.data() + 9); } // Decrement an iterator multiple times if constexpr (std::ranges::common_range) { std::array array{1, 2, 1, 2, 1}; ChunkByView view = make_chunk_by_view(array); ChunkByIterator it = view.end(); --it; --it; --it; assert(base((*it).begin()) == array.data()); } // Test with a predicate that takes by non-const reference if constexpr (!std::to_underlying(Constant)) { std::array array{1, 2, 3, -3, -2, -1}; View v{Iter{array.data()}, Sent{Iter{array.data() + array.size()}}}; auto view = std::views::chunk_by(std::move(v), [](int& x, int& y) { return x <= y; }); auto it = std::ranges::next(view.begin()); assert(base((*it).begin()) == array.data() + 3); --it; assert(base((*it).begin()) == array.data()); } // Test with a predicate that is invocable but not callable (i.e. cannot be called like regular function 'f()') { std::array array = {1, 2, 3, -3, -2, -1}; auto v = View{Iter{array.data()}, Sent{Iter{array.data() + array.size()}}} | std::views::transform([](int x) { return IntWrapper{x}; }); auto view = std::views::chunk_by(std::move(v), &IntWrapper::lessEqual); auto it = std::ranges::next(view.begin()); assert(base((*it).begin().base()) == array.data() + 3); --it; assert(base((*it).begin().base()) == array.data()); } // Make sure we do not make a copy of the predicate when we decrement if constexpr (std::ranges::common_range) { bool moved = false, copied = false; std::array array{1, 2, 1, 3}; View v{Iter(array.data()), Sent(Iter(array.data() + array.size()))}; auto view = std::views::chunk_by(std::move(v), TrackingPred(&moved, &copied)); assert(std::exchange(moved, false)); auto it = view.end(); --it; it--; assert(!moved); assert(!copied); } // Check post-decrement { std::array array{0, 1, 2, -3, -2, -1, -6, -5, -4}; ChunkByView view = make_chunk_by_view(array); ChunkByIterator it = std::ranges::next(view.begin(), view.end()); std::same_as decltype(auto) result = it--; assert(result != it); assert(result == std::default_sentinel); assert(base((*it).begin()) == array.data() + 6); result = it--; assert(base((*it).begin()) == array.data() + 3); assert(base((*result).begin()) == array.data() + 6); result = it--; assert(base((*it).begin()) == array.data()); assert(base((*result).begin()) == array.data() + 3); } } template constexpr void test_with_pair() { // Test with pair of iterators test(); // Test with iterator-sentinel pair test(); } constexpr bool tests() { test_with_pair, IsConst::no>(); test_with_pair, IsConst::no>(); test_with_pair, IsConst::no>(); test_with_pair(); test_with_pair, IsConst::yes>(); test_with_pair, IsConst::yes>(); test_with_pair, IsConst::yes>(); test_with_pair(); // Make sure `operator--` isn't provided for non bidirectional ranges { using ForwardView = View, sentinel_wrapper>>; using ChunkByView = std::ranges::chunk_by_view; static_assert(!HasPreDecrement>); static_assert(!HasPostDecrement>); } return true; } int main(int, char**) { tests(); static_assert(tests()); return 0; }