//===----------------------------------------------------------------------===// // // 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 // template // requires sized_sentinel_for, ziperator> // friend constexpr range_difference_t> // operator-(const sentinel& x, const iterator& y); #include #include #include #include #include #include "../types.h" template struct convertible_forward_sized_iterator { Base it_ = nullptr; using iterator_category = std::forward_iterator_tag; using value_type = int; using difference_type = std::intptr_t; convertible_forward_sized_iterator() = default; constexpr convertible_forward_sized_iterator(Base it) : it_(it) {} template U> constexpr convertible_forward_sized_iterator(const convertible_forward_sized_iterator& it) : it_(it.it_) {} constexpr decltype(*Base{}) operator*() const { return *it_; } constexpr convertible_forward_sized_iterator& operator++() { ++it_; return *this; } constexpr convertible_forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); } friend constexpr bool operator==(const convertible_forward_sized_iterator&, const convertible_forward_sized_iterator&) = default; friend constexpr difference_type operator-(const convertible_forward_sized_iterator& x, const convertible_forward_sized_iterator& y) { return x.it_ - y.it_; } }; static_assert(std::forward_iterator>); template struct convertible_sized_sentinel { Base base_; explicit convertible_sized_sentinel() = default; constexpr convertible_sized_sentinel(const Base& it) : base_(it) {} template U> constexpr convertible_sized_sentinel(const convertible_sized_sentinel& other) : base_(other.base_) {} template requires(std::convertible_to || std::convertible_to) friend constexpr bool operator==(const convertible_sized_sentinel& s, const U& base) { return s.base_ == base; } template requires(std::convertible_to || std::convertible_to) friend constexpr auto operator-(const convertible_sized_sentinel& s, const U& i) { return s.base_ - i; } template requires(std::convertible_to || std::convertible_to) friend constexpr auto operator-(const U& i, const convertible_sized_sentinel& s) { return i - s.base_; } }; static_assert(std::sized_sentinel_for>, convertible_forward_sized_iterator<>>); static_assert(std::sized_sentinel_for>, convertible_forward_sized_iterator>); static_assert(std::sized_sentinel_for>, convertible_forward_sized_iterator>); struct ConstCompatibleForwardSized : IntBufferView { using IntBufferView::IntBufferView; using iterator = convertible_forward_sized_iterator; using const_iterator = convertible_forward_sized_iterator; constexpr iterator begin() { return {buffer_}; } constexpr const_iterator begin() const { return {buffer_}; } constexpr convertible_sized_sentinel end() { return iterator{buffer_ + size_}; } constexpr convertible_sized_sentinel end() const { return const_iterator{buffer_ + size_}; } }; template concept HasMinus = std::invocable, const T&, const U&>; template concept SentinelHasMinus = HasMinus, std::ranges::iterator_t>; constexpr bool test() { int buffer1[5] = {1, 2, 3, 4, 5}; int buffer2[3] = {1, 2, 3}; { // shortest range std::ranges::zip_transform_view v(MakeTuple{}, std::views::iota(0, 3), ForwardSizedNonCommon(buffer1)); static_assert(!std::ranges::common_range); auto it = v.begin(); auto st = v.end(); assert(st - it == 3); assert(st - std::ranges::next(it, 1) == 2); assert(it - st == -3); assert(std::ranges::next(it, 1) - st == -2); static_assert(SentinelHasMinus); } { // underlying sentinel does not model sized_sentinel_for std::ranges::zip_transform_view v(MakeTuple{}, std::views::iota(0), SizedRandomAccessView(buffer1)); static_assert(!std::ranges::common_range); static_assert(!SentinelHasMinus); } { // const incompatible: // underlying const sentinels cannot subtract underlying iterators // underlying sentinels cannot subtract underlying const iterators std::ranges::zip_transform_view v(MakeTuple{}, NonSimpleForwardSizedNonCommon{buffer1}); static_assert(!std::ranges::common_range); static_assert(!simple_view); using Iter = std::ranges::iterator_t; using ConstIter = std::ranges::iterator_t; static_assert(!std::is_same_v); using Sentinel = std::ranges::sentinel_t; using ConstSentinel = std::ranges::sentinel_t; static_assert(!std::is_same_v); static_assert(HasMinus); static_assert(HasMinus); static_assert(HasMinus); static_assert(HasMinus); auto it = v.begin(); auto const_it = std::as_const(v).begin(); auto st = v.end(); auto const_st = std::as_const(v).end(); assert(it - st == -5); assert(st - it == 5); assert(const_it - const_st == -5); assert(const_st - const_it == 5); static_assert(!HasMinus); static_assert(!HasMinus); static_assert(!HasMinus); static_assert(!HasMinus); } { // const compatible allow non-const to const conversion std::ranges::zip_transform_view v(MakeTuple{}, ConstCompatibleForwardSized{buffer1}); static_assert(!std::ranges::common_range); static_assert(!simple_view); using Iter = std::ranges::iterator_t; using ConstIter = std::ranges::iterator_t; static_assert(!std::is_same_v); using Sentinel = std::ranges::sentinel_t; using ConstSentinel = std::ranges::sentinel_t; static_assert(!std::is_same_v); static_assert(HasMinus); static_assert(HasMinus); static_assert(HasMinus); static_assert(HasMinus); static_assert(HasMinus); static_assert(HasMinus); static_assert(HasMinus); static_assert(HasMinus); auto it = v.begin(); auto const_it = std::as_const(v).begin(); auto st = v.end(); auto const_st = std::as_const(v).end(); assert(it - st == -5); assert(st - it == 5); assert(const_it - const_st == -5); assert(const_st - const_it == 5); assert(it - const_st == -5); assert(const_st - it == 5); assert(const_it - st == -5); assert(st - const_it == 5); } auto testMinus = [](auto&& v, auto distance) { auto it = v.begin(); auto const_it = std::as_const(v).begin(); auto st = v.end(); auto const_st = std::as_const(v).end(); assert(it - st == -distance); assert(st - it == distance); assert(const_it - const_st == -distance); assert(const_st - const_it == distance); assert(it - const_st == -distance); assert(const_st - it == distance); assert(const_it - st == -distance); assert(st - const_it == distance); }; { // one range std::ranges::zip_transform_view v(MakeTuple{}, ConstCompatibleForwardSized{buffer1}); testMinus(v, 5); } { // two ranges std::ranges::zip_transform_view v(GetFirst{}, ConstCompatibleForwardSized{buffer1}, std::views::iota(0, 100)); testMinus(v, 5); } { // three ranges std::ranges::zip_transform_view v( Tie{}, ConstCompatibleForwardSized{buffer1}, ConstCompatibleForwardSized{buffer2}, std::ranges::single_view(2.)); testMinus(v, 1); } { // single empty range std::ranges::zip_transform_view v(MakeTuple{}, ConstCompatibleForwardSized(nullptr, 0)); testMinus(v, 0); } { // empty range at the beginning std::ranges::zip_transform_view v( MakeTuple{}, std::ranges::empty_view(), ConstCompatibleForwardSized{buffer1}, SimpleCommon{buffer2}); testMinus(v, 0); } { // empty range in the middle std::ranges::zip_transform_view v( MakeTuple{}, ConstCompatibleForwardSized{buffer1}, std::ranges::empty_view(), ConstCompatibleForwardSized{buffer2}); testMinus(v, 0); } { // empty range at the end std::ranges::zip_transform_view v( MakeTuple{}, SimpleCommon{buffer1}, ConstCompatibleForwardSized{buffer2}, std::ranges::empty_view()); testMinus(v, 0); } return true; } int main(int, char**) { test(); static_assert(test()); return 0; }