| //===----------------------------------------------------------------------===// |
| // |
| // 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 |
| |
| // std::ranges::rbegin |
| // std::ranges::crbegin |
| |
| #include <ranges> |
| |
| #include <cassert> |
| #include <utility> |
| #include "test_macros.h" |
| #include "test_iterators.h" |
| |
| using RangeRBeginT = decltype(std::ranges::rbegin); |
| using RangeCRBeginT = decltype(std::ranges::crbegin); |
| |
| static int globalBuff[8]; |
| |
| static_assert(!std::is_invocable_v<RangeRBeginT, int (&&)[10]>); |
| static_assert( std::is_invocable_v<RangeRBeginT, int (&)[10]>); |
| static_assert(!std::is_invocable_v<RangeRBeginT, int (&&)[]>); |
| static_assert(!std::is_invocable_v<RangeRBeginT, int (&)[]>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, int (&&)[10]>); |
| static_assert( std::is_invocable_v<RangeCRBeginT, int (&)[10]>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, int (&&)[]>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, int (&)[]>); |
| |
| struct Incomplete; |
| |
| static_assert(!std::is_invocable_v<RangeRBeginT, Incomplete(&&)[]>); |
| static_assert(!std::is_invocable_v<RangeRBeginT, const Incomplete(&&)[]>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, Incomplete(&&)[]>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&&)[]>); |
| |
| static_assert(!std::is_invocable_v<RangeRBeginT, Incomplete(&&)[10]>); |
| static_assert(!std::is_invocable_v<RangeRBeginT, const Incomplete(&&)[10]>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, Incomplete(&&)[10]>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&&)[10]>); |
| |
| // This case is IFNDR; we handle it SFINAE-friendly. |
| LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, Incomplete(&)[]>); |
| LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, const Incomplete(&)[]>); |
| LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[]>); |
| LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[]>); |
| |
| // This case is IFNDR; we handle it SFINAE-friendly. |
| LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, Incomplete(&)[10]>); |
| LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, const Incomplete(&)[10]>); |
| LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[10]>); |
| LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[10]>); |
| |
| struct RBeginMember { |
| int x; |
| constexpr const int *rbegin() const { return &x; } |
| }; |
| |
| // Ensure that we can't call with rvalues with borrowing disabled. |
| static_assert( std::is_invocable_v<RangeRBeginT, RBeginMember &>); |
| static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMember &&>); |
| static_assert( std::is_invocable_v<RangeRBeginT, RBeginMember const&>); |
| static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMember const&&>); |
| static_assert( std::is_invocable_v<RangeCRBeginT, RBeginMember &>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember &&>); |
| static_assert( std::is_invocable_v<RangeCRBeginT, RBeginMember const&>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember const&&>); |
| |
| constexpr bool testReturnTypes() { |
| { |
| int *x[2]; |
| ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator<int**>); |
| ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator<int* const*>); |
| } |
| { |
| int x[2][2]; |
| ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator<int(*)[2]>); |
| ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator<const int(*)[2]>); |
| } |
| { |
| struct Different { |
| char*& rbegin(); |
| short*& rbegin() const; |
| } x; |
| ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), char*); |
| ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), short*); |
| } |
| return true; |
| } |
| |
| constexpr bool testArray() { |
| int a[2]; |
| assert(std::ranges::rbegin(a).base() == a + 2); |
| assert(std::ranges::crbegin(a).base() == a + 2); |
| |
| int b[2][2]; |
| assert(std::ranges::rbegin(b).base() == b + 2); |
| assert(std::ranges::crbegin(b).base() == b + 2); |
| |
| RBeginMember c[2]; |
| assert(std::ranges::rbegin(c).base() == c + 2); |
| assert(std::ranges::crbegin(c).base() == c + 2); |
| |
| return true; |
| } |
| |
| struct RBeginMemberReturnsInt { |
| int rbegin() const; |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMemberReturnsInt const&>); |
| |
| struct RBeginMemberReturnsVoidPtr { |
| const void *rbegin() const; |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMemberReturnsVoidPtr const&>); |
| |
| struct PtrConvertibleRBeginMember { |
| struct iterator { operator int*() const; }; |
| iterator rbegin() const; |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, PtrConvertibleRBeginMember const&>); |
| |
| struct NonConstRBeginMember { |
| int x; |
| constexpr int* rbegin() { return &x; } |
| }; |
| static_assert( std::is_invocable_v<RangeRBeginT, NonConstRBeginMember &>); |
| static_assert(!std::is_invocable_v<RangeRBeginT, NonConstRBeginMember const&>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember &>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember const&>); |
| |
| struct EnabledBorrowingRBeginMember { |
| constexpr int *rbegin() const { return globalBuff; } |
| }; |
| template<> |
| inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingRBeginMember> = true; |
| |
| struct RBeginMemberFunction { |
| int x; |
| constexpr const int *rbegin() const { return &x; } |
| friend int* rbegin(RBeginMemberFunction const&); |
| }; |
| |
| struct EmptyPtrRBeginMember { |
| struct Empty {}; |
| Empty x; |
| constexpr const Empty* rbegin() const { return &x; } |
| }; |
| |
| constexpr bool testRBeginMember() { |
| RBeginMember a; |
| assert(std::ranges::rbegin(a) == &a.x); |
| assert(std::ranges::crbegin(a) == &a.x); |
| static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMember&&>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember&&>); |
| |
| NonConstRBeginMember b; |
| assert(std::ranges::rbegin(b) == &b.x); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember&>); |
| |
| EnabledBorrowingRBeginMember c; |
| assert(std::ranges::rbegin(c) == globalBuff); |
| assert(std::ranges::crbegin(c) == globalBuff); |
| assert(std::ranges::rbegin(std::move(c)) == globalBuff); |
| assert(std::ranges::crbegin(std::move(c)) == globalBuff); |
| |
| RBeginMemberFunction d; |
| assert(std::ranges::rbegin(d) == &d.x); |
| assert(std::ranges::crbegin(d) == &d.x); |
| |
| EmptyPtrRBeginMember e; |
| assert(std::ranges::rbegin(e) == &e.x); |
| assert(std::ranges::crbegin(e) == &e.x); |
| |
| return true; |
| } |
| |
| |
| struct RBeginFunction { |
| int x; |
| friend constexpr const int* rbegin(RBeginFunction const& bf) { return &bf.x; } |
| }; |
| static_assert( std::is_invocable_v<RangeRBeginT, RBeginFunction const&>); |
| static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunction &&>); |
| static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunction &>); |
| static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction const&>); |
| static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction &>); |
| |
| struct RBeginFunctionReturnsInt { |
| friend int rbegin(RBeginFunctionReturnsInt const&); |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsInt const&>); |
| |
| struct RBeginFunctionReturnsVoidPtr { |
| friend void *rbegin(RBeginFunctionReturnsVoidPtr const&); |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsVoidPtr const&>); |
| |
| struct RBeginFunctionReturnsEmpty { |
| struct Empty {}; |
| friend Empty rbegin(RBeginFunctionReturnsEmpty const&); |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsEmpty const&>); |
| |
| struct RBeginFunctionReturnsPtrConvertible { |
| struct iterator { operator int*() const; }; |
| friend iterator rbegin(RBeginFunctionReturnsPtrConvertible const&); |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsPtrConvertible const&>); |
| |
| struct RBeginFunctionByValue { |
| friend constexpr int *rbegin(RBeginFunctionByValue) { return globalBuff + 1; } |
| }; |
| static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginFunctionByValue>); |
| |
| struct RBeginFunctionEnabledBorrowing { |
| friend constexpr int *rbegin(RBeginFunctionEnabledBorrowing) { return globalBuff + 2; } |
| }; |
| template<> |
| inline constexpr bool std::ranges::enable_borrowed_range<RBeginFunctionEnabledBorrowing> = true; |
| |
| struct RBeginFunctionReturnsEmptyPtr { |
| struct Empty {}; |
| Empty x; |
| friend constexpr const Empty *rbegin(RBeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; } |
| }; |
| |
| struct RBeginFunctionWithDataMember { |
| int x; |
| int rbegin; |
| friend constexpr const int *rbegin(RBeginFunctionWithDataMember const& bf) { return &bf.x; } |
| }; |
| |
| struct RBeginFunctionWithPrivateBeginMember { |
| int y; |
| friend constexpr const int *rbegin(RBeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; } |
| private: |
| const int *rbegin() const; |
| }; |
| |
| constexpr bool testRBeginFunction() { |
| RBeginFunction a{}; |
| const RBeginFunction aa{}; |
| static_assert(!std::invocable<RangeRBeginT, decltype((a))>); |
| assert(std::ranges::crbegin(a) == &a.x); |
| assert(std::ranges::rbegin(aa) == &aa.x); |
| assert(std::ranges::crbegin(aa) == &aa.x); |
| |
| RBeginFunctionByValue b{}; |
| const RBeginFunctionByValue bb{}; |
| assert(std::ranges::rbegin(b) == globalBuff + 1); |
| assert(std::ranges::crbegin(b) == globalBuff + 1); |
| assert(std::ranges::rbegin(bb) == globalBuff + 1); |
| assert(std::ranges::crbegin(bb) == globalBuff + 1); |
| |
| RBeginFunctionEnabledBorrowing c{}; |
| const RBeginFunctionEnabledBorrowing cc{}; |
| assert(std::ranges::rbegin(std::move(c)) == globalBuff + 2); |
| assert(std::ranges::crbegin(std::move(c)) == globalBuff + 2); |
| assert(std::ranges::rbegin(std::move(cc)) == globalBuff + 2); |
| assert(std::ranges::crbegin(std::move(cc)) == globalBuff + 2); |
| |
| RBeginFunctionReturnsEmptyPtr d{}; |
| const RBeginFunctionReturnsEmptyPtr dd{}; |
| static_assert(!std::invocable<RangeRBeginT, decltype((d))>); |
| assert(std::ranges::crbegin(d) == &d.x); |
| assert(std::ranges::rbegin(dd) == &dd.x); |
| assert(std::ranges::crbegin(dd) == &dd.x); |
| |
| RBeginFunctionWithDataMember e{}; |
| const RBeginFunctionWithDataMember ee{}; |
| static_assert(!std::invocable<RangeRBeginT, decltype((e))>); |
| assert(std::ranges::rbegin(ee) == &ee.x); |
| assert(std::ranges::crbegin(e) == &e.x); |
| assert(std::ranges::crbegin(ee) == &ee.x); |
| |
| RBeginFunctionWithPrivateBeginMember f{}; |
| const RBeginFunctionWithPrivateBeginMember ff{}; |
| static_assert(!std::invocable<RangeRBeginT, decltype((f))>); |
| assert(std::ranges::crbegin(f) == &f.y); |
| assert(std::ranges::rbegin(ff) == &ff.y); |
| assert(std::ranges::crbegin(ff) == &ff.y); |
| |
| return true; |
| } |
| |
| |
| struct MemberBeginEnd { |
| int b, e; |
| char cb, ce; |
| constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); } |
| constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); } |
| constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); } |
| constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); } |
| }; |
| static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd&>); |
| static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd const&>); |
| static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginEnd const&>); |
| |
| struct FunctionBeginEnd { |
| int b, e; |
| char cb, ce; |
| friend constexpr bidirectional_iterator<int*> begin(FunctionBeginEnd& v) { |
| return bidirectional_iterator<int*>(&v.b); |
| } |
| friend constexpr bidirectional_iterator<int*> end(FunctionBeginEnd& v) { return bidirectional_iterator<int*>(&v.e); } |
| friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginEnd& v) { |
| return bidirectional_iterator<const char*>(&v.cb); |
| } |
| friend constexpr bidirectional_iterator<const char*> end(const FunctionBeginEnd& v) { |
| return bidirectional_iterator<const char*>(&v.ce); |
| } |
| }; |
| static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd&>); |
| static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd const&>); |
| static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginEnd const&>); |
| |
| struct MemberBeginFunctionEnd { |
| int b, e; |
| char cb, ce; |
| constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); } |
| friend constexpr bidirectional_iterator<int*> end(MemberBeginFunctionEnd& v) { |
| return bidirectional_iterator<int*>(&v.e); |
| } |
| constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); } |
| friend constexpr bidirectional_iterator<const char*> end(const MemberBeginFunctionEnd& v) { |
| return bidirectional_iterator<const char*>(&v.ce); |
| } |
| }; |
| static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd&>); |
| static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd const&>); |
| static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginFunctionEnd const&>); |
| |
| struct FunctionBeginMemberEnd { |
| int b, e; |
| char cb, ce; |
| friend constexpr bidirectional_iterator<int*> begin(FunctionBeginMemberEnd& v) { |
| return bidirectional_iterator<int*>(&v.b); |
| } |
| constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); } |
| friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginMemberEnd& v) { |
| return bidirectional_iterator<const char*>(&v.cb); |
| } |
| constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); } |
| }; |
| static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd&>); |
| static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd const&>); |
| static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginMemberEnd const&>); |
| |
| struct MemberBeginEndDifferentTypes { |
| bidirectional_iterator<int*> begin(); |
| bidirectional_iterator<const int*> end(); |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndDifferentTypes&>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndDifferentTypes&>); |
| |
| struct FunctionBeginEndDifferentTypes { |
| friend bidirectional_iterator<int*> begin(FunctionBeginEndDifferentTypes&); |
| friend bidirectional_iterator<const int*> end(FunctionBeginEndDifferentTypes&); |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndDifferentTypes&>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndDifferentTypes&>); |
| |
| struct MemberBeginEndForwardIterators { |
| forward_iterator<int*> begin(); |
| forward_iterator<int*> end(); |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndForwardIterators&>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndForwardIterators&>); |
| |
| struct FunctionBeginEndForwardIterators { |
| friend forward_iterator<int*> begin(FunctionBeginEndForwardIterators&); |
| friend forward_iterator<int*> end(FunctionBeginEndForwardIterators&); |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndForwardIterators&>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndForwardIterators&>); |
| |
| struct MemberBeginOnly { |
| bidirectional_iterator<int*> begin() const; |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginOnly&>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginOnly&>); |
| |
| struct FunctionBeginOnly { |
| friend bidirectional_iterator<int*> begin(FunctionBeginOnly&); |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginOnly&>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginOnly&>); |
| |
| struct MemberEndOnly { |
| bidirectional_iterator<int*> end() const; |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, MemberEndOnly&>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, MemberEndOnly&>); |
| |
| struct FunctionEndOnly { |
| friend bidirectional_iterator<int*> end(FunctionEndOnly&); |
| }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, FunctionEndOnly&>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionEndOnly&>); |
| |
| // Make sure there is no clash between the following cases: |
| // - the case that handles classes defining member `rbegin` and `rend` functions; |
| // - the case that handles classes defining `begin` and `end` functions returning reversible iterators. |
| struct MemberBeginAndRBegin { |
| int* begin() const; |
| int* end() const; |
| int* rbegin() const; |
| int* rend() const; |
| }; |
| static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginAndRBegin&>); |
| static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginAndRBegin&>); |
| static_assert( std::same_as<std::invoke_result_t<RangeRBeginT, MemberBeginAndRBegin&>, int*>); |
| static_assert( std::same_as<std::invoke_result_t<RangeCRBeginT, MemberBeginAndRBegin&>, int*>); |
| |
| constexpr bool testBeginEnd() { |
| MemberBeginEnd a{}; |
| const MemberBeginEnd aa{}; |
| assert(base(std::ranges::rbegin(a).base()) == &a.e); |
| assert(base(std::ranges::crbegin(a).base()) == &a.ce); |
| assert(base(std::ranges::rbegin(aa).base()) == &aa.ce); |
| assert(base(std::ranges::crbegin(aa).base()) == &aa.ce); |
| |
| FunctionBeginEnd b{}; |
| const FunctionBeginEnd bb{}; |
| assert(base(std::ranges::rbegin(b).base()) == &b.e); |
| assert(base(std::ranges::crbegin(b).base()) == &b.ce); |
| assert(base(std::ranges::rbegin(bb).base()) == &bb.ce); |
| assert(base(std::ranges::crbegin(bb).base()) == &bb.ce); |
| |
| MemberBeginFunctionEnd c{}; |
| const MemberBeginFunctionEnd cc{}; |
| assert(base(std::ranges::rbegin(c).base()) == &c.e); |
| assert(base(std::ranges::crbegin(c).base()) == &c.ce); |
| assert(base(std::ranges::rbegin(cc).base()) == &cc.ce); |
| assert(base(std::ranges::crbegin(cc).base()) == &cc.ce); |
| |
| FunctionBeginMemberEnd d{}; |
| const FunctionBeginMemberEnd dd{}; |
| assert(base(std::ranges::rbegin(d).base()) == &d.e); |
| assert(base(std::ranges::crbegin(d).base()) == &d.ce); |
| assert(base(std::ranges::rbegin(dd).base()) == &dd.ce); |
| assert(base(std::ranges::crbegin(dd).base()) == &dd.ce); |
| |
| return true; |
| } |
| |
| |
| ASSERT_NOEXCEPT(std::ranges::rbegin(std::declval<int (&)[10]>())); |
| ASSERT_NOEXCEPT(std::ranges::crbegin(std::declval<int (&)[10]>())); |
| |
| struct NoThrowMemberRBegin { |
| ThrowingIterator<int> rbegin() const noexcept; // auto(t.rbegin()) doesn't throw |
| } ntmb; |
| static_assert(noexcept(std::ranges::rbegin(ntmb))); |
| static_assert(noexcept(std::ranges::crbegin(ntmb))); |
| |
| struct NoThrowADLRBegin { |
| friend ThrowingIterator<int> rbegin(NoThrowADLRBegin&) noexcept; // auto(rbegin(t)) doesn't throw |
| friend ThrowingIterator<int> rbegin(const NoThrowADLRBegin&) noexcept; |
| } ntab; |
| static_assert(noexcept(std::ranges::rbegin(ntab))); |
| static_assert(noexcept(std::ranges::crbegin(ntab))); |
| |
| struct NoThrowMemberRBeginReturnsRef { |
| ThrowingIterator<int>& rbegin() const noexcept; // auto(t.rbegin()) may throw |
| } ntmbrr; |
| static_assert(!noexcept(std::ranges::rbegin(ntmbrr))); |
| static_assert(!noexcept(std::ranges::crbegin(ntmbrr))); |
| |
| struct RBeginReturnsArrayRef { |
| auto rbegin() const noexcept -> int(&)[10]; |
| } brar; |
| static_assert(noexcept(std::ranges::rbegin(brar))); |
| static_assert(noexcept(std::ranges::crbegin(brar))); |
| |
| struct NoThrowBeginThrowingEnd { |
| int* begin() const noexcept; |
| int* end() const; |
| } ntbte; |
| static_assert(!noexcept(std::ranges::rbegin(ntbte))); |
| static_assert(!noexcept(std::ranges::crbegin(ntbte))); |
| |
| struct NoThrowEndThrowingBegin { |
| int* begin() const; |
| int* end() const noexcept; |
| } ntetb; |
| static_assert(noexcept(std::ranges::rbegin(ntetb))); |
| static_assert(noexcept(std::ranges::crbegin(ntetb))); |
| |
| // Test ADL-proofing. |
| struct Incomplete; |
| template<class T> struct Holder { T t; }; |
| static_assert(!std::is_invocable_v<RangeRBeginT, Holder<Incomplete>*>); |
| static_assert(!std::is_invocable_v<RangeRBeginT, Holder<Incomplete>*&>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*>); |
| static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*&>); |
| |
| int main(int, char**) { |
| static_assert(testReturnTypes()); |
| |
| testArray(); |
| static_assert(testArray()); |
| |
| testRBeginMember(); |
| static_assert(testRBeginMember()); |
| |
| testRBeginFunction(); |
| static_assert(testRBeginFunction()); |
| |
| testBeginEnd(); |
| static_assert(testBeginEnd()); |
| |
| return 0; |
| } |