mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-14 03:45:33 +00:00
[libc++][ranges] Implement rbegin, rend, crbegin and crend.
Differential Revision: https://reviews.llvm.org/D119057
This commit is contained in:
parent
01e04867e8
commit
55bd22f853
@ -100,15 +100,15 @@ Section,Description,Dependencies,Assignee,Complete
|
||||
| `ranges::end <https://llvm.org/D100255>`_
|
||||
| `range::cbegin <https://llvm.org/D100255>`_
|
||||
| `ranges::cend <https://llvm.org/D100255>`_
|
||||
| ranges::rbegin
|
||||
| ranges::rend
|
||||
| ranges::crbegin
|
||||
| ranges::crend
|
||||
| `ranges::rbegin <https://llvm.org/D119057>`_
|
||||
| `ranges::rend <https://llvm.org/D119057>`_
|
||||
| `ranges::crbegin <https://llvm.org/D119057>`_
|
||||
| `ranges::crend <https://llvm.org/D119057>`_
|
||||
| `ranges::size <https://llvm.org/D101079>`_
|
||||
| `ranges::ssize <https://llvm.org/D101189>`_
|
||||
| `ranges::empty <https://llvm.org/D101193>`_
|
||||
| `ranges::data <https://llvm.org/D101476>`_
|
||||
| `ranges::cdata <https://llvm.org/D117044>`_",[iterator.concepts],Christopher Di Bella and Zoe Carver,In progress
|
||||
| `ranges::cdata <https://llvm.org/D117044>`_",[iterator.concepts],Various,✅
|
||||
`[range.range] <https://wg21.link/range.range>`_,"| `ranges::range <https://llvm.org/D100269>`_
|
||||
| `ranges::borrowed_range <https://llvm.org/D102426>`_
|
||||
| `ranges::enable_borrowed_range <https://llvm.org/D90999>`_
|
||||
|
|
@ -346,7 +346,9 @@ set(files
|
||||
__ranges/non_propagating_cache.h
|
||||
__ranges/owning_view.h
|
||||
__ranges/range_adaptor.h
|
||||
__ranges/rbegin.h
|
||||
__ranges/ref_view.h
|
||||
__ranges/rend.h
|
||||
__ranges/reverse_view.h
|
||||
__ranges/single_view.h
|
||||
__ranges/size.h
|
||||
|
@ -26,6 +26,7 @@ template<class _Tp>
|
||||
concept __class_or_enum = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
|
||||
|
||||
// Work around Clang bug https://llvm.org/PR52970
|
||||
// TODO: remove this workaround once libc++ no longer has to support Clang 13 (it was fixed in Clang 14).
|
||||
template<class _Tp>
|
||||
concept __workaround_52970 = is_class_v<__uncvref_t<_Tp>> || is_union_v<__uncvref_t<_Tp>>;
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <__iterator/readable_traits.h>
|
||||
#include <__ranges/enable_borrowed_range.h>
|
||||
#include <__utility/auto_cast.h>
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
|
130
libcxx/include/__ranges/rbegin.h
Normal file
130
libcxx/include/__ranges/rbegin.h
Normal file
@ -0,0 +1,130 @@
|
||||
// -*- 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___RANGES_RBEGIN_H
|
||||
#define _LIBCPP___RANGES_RBEGIN_H
|
||||
|
||||
#include <__concepts/class_or_enum.h>
|
||||
#include <__concepts/same_as.h>
|
||||
#include <__config>
|
||||
#include <__iterator/concepts.h>
|
||||
#include <__iterator/readable_traits.h>
|
||||
#include <__iterator/reverse_iterator.h>
|
||||
#include <__ranges/access.h>
|
||||
#include <__utility/auto_cast.h>
|
||||
#include <type_traits>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
||||
|
||||
// [ranges.access.rbegin]
|
||||
|
||||
namespace ranges {
|
||||
namespace __rbegin {
|
||||
template <class _Tp>
|
||||
concept __member_rbegin =
|
||||
__can_borrow<_Tp> &&
|
||||
__workaround_52970<_Tp> &&
|
||||
requires(_Tp&& __t) {
|
||||
{ _LIBCPP_AUTO_CAST(__t.rbegin()) } -> input_or_output_iterator;
|
||||
};
|
||||
|
||||
void rbegin(auto&) = delete;
|
||||
void rbegin(const auto&) = delete;
|
||||
|
||||
template <class _Tp>
|
||||
concept __unqualified_rbegin =
|
||||
!__member_rbegin<_Tp> &&
|
||||
__can_borrow<_Tp> &&
|
||||
__class_or_enum<remove_cvref_t<_Tp>> &&
|
||||
requires(_Tp&& __t) {
|
||||
{ _LIBCPP_AUTO_CAST(rbegin(__t)) } -> input_or_output_iterator;
|
||||
};
|
||||
|
||||
template <class _Tp>
|
||||
concept __can_reverse =
|
||||
__can_borrow<_Tp> &&
|
||||
!__member_rbegin<_Tp> &&
|
||||
!__unqualified_rbegin<_Tp> &&
|
||||
requires(_Tp&& __t) {
|
||||
{ ranges::begin(__t) } -> same_as<decltype(ranges::end(__t))>;
|
||||
{ ranges::begin(__t) } -> bidirectional_iterator;
|
||||
};
|
||||
|
||||
struct __fn {
|
||||
template <class _Tp>
|
||||
requires __member_rbegin<_Tp>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.rbegin())))
|
||||
{
|
||||
return _LIBCPP_AUTO_CAST(__t.rbegin());
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
requires __unqualified_rbegin<_Tp>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(_LIBCPP_AUTO_CAST(rbegin(__t))))
|
||||
{
|
||||
return _LIBCPP_AUTO_CAST(rbegin(__t));
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
requires __can_reverse<_Tp>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(ranges::end(__t)))
|
||||
{
|
||||
return std::make_reverse_iterator(ranges::end(__t));
|
||||
}
|
||||
|
||||
void operator()(auto&&) const = delete;
|
||||
};
|
||||
} // namespace __rbegin
|
||||
|
||||
inline namespace __cpo {
|
||||
inline constexpr auto rbegin = __rbegin::__fn{};
|
||||
} // namespace __cpo
|
||||
} // namespace ranges
|
||||
|
||||
// [range.access.crbegin]
|
||||
|
||||
namespace ranges {
|
||||
namespace __crbegin {
|
||||
struct __fn {
|
||||
template <class _Tp>
|
||||
requires is_lvalue_reference_v<_Tp&&>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(ranges::rbegin(static_cast<const remove_reference_t<_Tp>&>(__t))))
|
||||
-> decltype( ranges::rbegin(static_cast<const remove_reference_t<_Tp>&>(__t)))
|
||||
{ return ranges::rbegin(static_cast<const remove_reference_t<_Tp>&>(__t)); }
|
||||
|
||||
template <class _Tp>
|
||||
requires is_rvalue_reference_v<_Tp&&>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(ranges::rbegin(static_cast<const _Tp&&>(__t))))
|
||||
-> decltype( ranges::rbegin(static_cast<const _Tp&&>(__t)))
|
||||
{ return ranges::rbegin(static_cast<const _Tp&&>(__t)); }
|
||||
};
|
||||
} // namespace __crbegin
|
||||
|
||||
inline namespace __cpo {
|
||||
inline constexpr auto crbegin = __crbegin::__fn{};
|
||||
} // namespace __cpo
|
||||
} // namespace ranges
|
||||
|
||||
#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___RANGES_RBEGIN_H
|
134
libcxx/include/__ranges/rend.h
Normal file
134
libcxx/include/__ranges/rend.h
Normal file
@ -0,0 +1,134 @@
|
||||
// -*- 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___RANGES_REND_H
|
||||
#define _LIBCPP___RANGES_REND_H
|
||||
|
||||
#include <__concepts/class_or_enum.h>
|
||||
#include <__concepts/same_as.h>
|
||||
#include <__config>
|
||||
#include <__iterator/concepts.h>
|
||||
#include <__iterator/readable_traits.h>
|
||||
#include <__iterator/reverse_iterator.h>
|
||||
#include <__ranges/access.h>
|
||||
#include <__ranges/rbegin.h>
|
||||
#include <__utility/auto_cast.h>
|
||||
#include <type_traits>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
||||
|
||||
// [range.access.rend]
|
||||
|
||||
namespace ranges {
|
||||
namespace __rend {
|
||||
template <class _Tp>
|
||||
concept __member_rend =
|
||||
__can_borrow<_Tp> &&
|
||||
__workaround_52970<_Tp> &&
|
||||
requires(_Tp&& __t) {
|
||||
ranges::rbegin(__t);
|
||||
{ _LIBCPP_AUTO_CAST(__t.rend()) } -> sentinel_for<decltype(ranges::rbegin(__t))>;
|
||||
};
|
||||
|
||||
void rend(auto&) = delete;
|
||||
void rend(const auto&) = delete;
|
||||
|
||||
template <class _Tp>
|
||||
concept __unqualified_rend =
|
||||
!__member_rend<_Tp> &&
|
||||
__can_borrow<_Tp> &&
|
||||
__class_or_enum<remove_cvref_t<_Tp>> &&
|
||||
requires(_Tp&& __t) {
|
||||
ranges::rbegin(__t);
|
||||
{ _LIBCPP_AUTO_CAST(rend(__t)) } -> sentinel_for<decltype(ranges::rbegin(__t))>;
|
||||
};
|
||||
|
||||
template <class _Tp>
|
||||
concept __can_reverse =
|
||||
__can_borrow<_Tp> &&
|
||||
!__member_rend<_Tp> &&
|
||||
!__unqualified_rend<_Tp> &&
|
||||
requires(_Tp&& __t) {
|
||||
{ ranges::begin(__t) } -> same_as<decltype(ranges::end(__t))>;
|
||||
{ ranges::begin(__t) } -> bidirectional_iterator;
|
||||
};
|
||||
|
||||
class __fn {
|
||||
public:
|
||||
template <class _Tp>
|
||||
requires __member_rend<_Tp>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.rend())))
|
||||
{
|
||||
return _LIBCPP_AUTO_CAST(__t.rend());
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
requires __unqualified_rend<_Tp>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(_LIBCPP_AUTO_CAST(rend(__t))))
|
||||
{
|
||||
return _LIBCPP_AUTO_CAST(rend(__t));
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
requires __can_reverse<_Tp>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(ranges::begin(__t)))
|
||||
{
|
||||
return std::make_reverse_iterator(ranges::begin(__t));
|
||||
}
|
||||
|
||||
void operator()(auto&&) const = delete;
|
||||
};
|
||||
} // namespace __rend
|
||||
|
||||
inline namespace __cpo {
|
||||
inline constexpr auto rend = __rend::__fn{};
|
||||
} // namespace __cpo
|
||||
} // namespace ranges
|
||||
|
||||
// [range.access.crend]
|
||||
|
||||
namespace ranges {
|
||||
namespace __crend {
|
||||
struct __fn {
|
||||
template <class _Tp>
|
||||
requires is_lvalue_reference_v<_Tp&&>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(ranges::rend(static_cast<const remove_reference_t<_Tp>&>(__t))))
|
||||
-> decltype( ranges::rend(static_cast<const remove_reference_t<_Tp>&>(__t)))
|
||||
{ return ranges::rend(static_cast<const remove_reference_t<_Tp>&>(__t)); }
|
||||
|
||||
template <class _Tp>
|
||||
requires is_rvalue_reference_v<_Tp&&>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(ranges::rend(static_cast<const _Tp&&>(__t))))
|
||||
-> decltype( ranges::rend(static_cast<const _Tp&&>(__t)))
|
||||
{ return ranges::rend(static_cast<const _Tp&&>(__t)); }
|
||||
};
|
||||
} // namespace __crend
|
||||
|
||||
inline namespace __cpo {
|
||||
inline constexpr auto crend = __crend::__fn{};
|
||||
} // namespace __cpo
|
||||
} // namespace ranges
|
||||
|
||||
#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___RANGES_REND_H
|
@ -815,7 +815,9 @@ module std [system] {
|
||||
module non_propagating_cache { private header "__ranges/non_propagating_cache.h" }
|
||||
module owning_view { private header "__ranges/owning_view.h" }
|
||||
module range_adaptor { private header "__ranges/range_adaptor.h" }
|
||||
module rbegin { private header "__ranges/rbegin.h" }
|
||||
module ref_view { private header "__ranges/ref_view.h" }
|
||||
module rend { private header "__ranges/rend.h" }
|
||||
module reverse_view { private header "__ranges/reverse_view.h" }
|
||||
module single_view { private header "__ranges/single_view.h" }
|
||||
module size { private header "__ranges/size.h" }
|
||||
|
@ -250,7 +250,9 @@ namespace std {
|
||||
#include <__ranges/enable_view.h>
|
||||
#include <__ranges/iota_view.h>
|
||||
#include <__ranges/join_view.h>
|
||||
#include <__ranges/rbegin.h>
|
||||
#include <__ranges/ref_view.h>
|
||||
#include <__ranges/rend.h>
|
||||
#include <__ranges/reverse_view.h>
|
||||
#include <__ranges/single_view.h>
|
||||
#include <__ranges/size.h>
|
||||
|
@ -0,0 +1,15 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: modules-build
|
||||
|
||||
// WARNING: This test was generated by 'generate_private_header_tests.py'
|
||||
// and should not be edited manually.
|
||||
|
||||
// expected-error@*:* {{use of private header from outside its module: '__ranges/rbegin.h'}}
|
||||
#include <__ranges/rbegin.h>
|
@ -0,0 +1,15 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: modules-build
|
||||
|
||||
// WARNING: This test was generated by 'generate_private_header_tests.py'
|
||||
// and should not be edited manually.
|
||||
|
||||
// expected-error@*:* {{use of private header from outside its module: '__ranges/rend.h'}}
|
||||
#include <__ranges/rend.h>
|
522
libcxx/test/std/ranges/range.access/rbegin.pass.cpp
Normal file
522
libcxx/test/std/ranges/range.access/rbegin.pass.cpp
Normal file
@ -0,0 +1,522 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// 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(std::ranges::rbegin(a).base().base() == &a.e);
|
||||
assert(std::ranges::crbegin(a).base().base() == &a.ce);
|
||||
assert(std::ranges::rbegin(aa).base().base() == &aa.ce);
|
||||
assert(std::ranges::crbegin(aa).base().base() == &aa.ce);
|
||||
|
||||
FunctionBeginEnd b{};
|
||||
const FunctionBeginEnd bb{};
|
||||
assert(std::ranges::rbegin(b).base().base() == &b.e);
|
||||
assert(std::ranges::crbegin(b).base().base() == &b.ce);
|
||||
assert(std::ranges::rbegin(bb).base().base() == &bb.ce);
|
||||
assert(std::ranges::crbegin(bb).base().base() == &bb.ce);
|
||||
|
||||
MemberBeginFunctionEnd c{};
|
||||
const MemberBeginFunctionEnd cc{};
|
||||
assert(std::ranges::rbegin(c).base().base() == &c.e);
|
||||
assert(std::ranges::crbegin(c).base().base() == &c.ce);
|
||||
assert(std::ranges::rbegin(cc).base().base() == &cc.ce);
|
||||
assert(std::ranges::crbegin(cc).base().base() == &cc.ce);
|
||||
|
||||
FunctionBeginMemberEnd d{};
|
||||
const FunctionBeginMemberEnd dd{};
|
||||
assert(std::ranges::rbegin(d).base().base() == &d.e);
|
||||
assert(std::ranges::crbegin(d).base().base() == &d.ce);
|
||||
assert(std::ranges::rbegin(dd).base().base() == &dd.ce);
|
||||
assert(std::ranges::crbegin(dd).base().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;
|
||||
}
|
551
libcxx/test/std/ranges/range.access/rend.pass.cpp
Normal file
551
libcxx/test/std/ranges/range.access/rend.pass.cpp
Normal file
@ -0,0 +1,551 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// std::ranges::rend
|
||||
// std::ranges::crend
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include "test_macros.h"
|
||||
#include "test_iterators.h"
|
||||
|
||||
using RangeREndT = decltype(std::ranges::rend);
|
||||
using RangeCREndT = decltype(std::ranges::crend);
|
||||
|
||||
static int globalBuff[8];
|
||||
|
||||
static_assert(!std::is_invocable_v<RangeREndT, int (&&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeREndT, int (&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeREndT, int (&&)[10]>);
|
||||
static_assert( std::is_invocable_v<RangeREndT, int (&)[10]>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, int (&&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, int (&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, int (&&)[10]>);
|
||||
static_assert( std::is_invocable_v<RangeCREndT, int (&)[10]>);
|
||||
|
||||
struct Incomplete;
|
||||
static_assert(!std::is_invocable_v<RangeREndT, Incomplete(&&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeREndT, Incomplete(&&)[42]>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, Incomplete(&&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, Incomplete(&&)[42]>);
|
||||
|
||||
struct REndMember {
|
||||
int x;
|
||||
const int* rbegin() const;
|
||||
constexpr const int* rend() const { return &x; }
|
||||
};
|
||||
|
||||
// Ensure that we can't call with rvalues with borrowing disabled.
|
||||
static_assert( std::is_invocable_v<RangeREndT, REndMember&>);
|
||||
static_assert(!std::is_invocable_v<RangeREndT, REndMember &&>);
|
||||
static_assert( std::is_invocable_v<RangeREndT, REndMember const&>);
|
||||
static_assert(!std::is_invocable_v<RangeREndT, REndMember const&&>);
|
||||
static_assert( std::is_invocable_v<RangeCREndT, REndMember &>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, REndMember &&>);
|
||||
static_assert( std::is_invocable_v<RangeCREndT, REndMember const&>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, REndMember const&&>);
|
||||
|
||||
constexpr bool testReturnTypes() {
|
||||
{
|
||||
int *x[2];
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator<int**>);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator<int* const*>);
|
||||
}
|
||||
|
||||
{
|
||||
int x[2][2];
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator<int(*)[2]>);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator<const int(*)[2]>);
|
||||
}
|
||||
|
||||
{
|
||||
struct Different {
|
||||
char* rbegin();
|
||||
sentinel_wrapper<char*>& rend();
|
||||
short* rbegin() const;
|
||||
sentinel_wrapper<short*>& rend() const;
|
||||
} x;
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), sentinel_wrapper<char*>);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), sentinel_wrapper<short*>);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool testArray() {
|
||||
int a[2];
|
||||
assert(std::ranges::rend(a).base() == a);
|
||||
assert(std::ranges::crend(a).base() == a);
|
||||
|
||||
int b[2][2];
|
||||
assert(std::ranges::rend(b).base() == b);
|
||||
assert(std::ranges::crend(b).base() == b);
|
||||
|
||||
REndMember c[2];
|
||||
assert(std::ranges::rend(c).base() == c);
|
||||
assert(std::ranges::crend(c).base() == c);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct REndMemberReturnsInt {
|
||||
int rbegin() const;
|
||||
int rend() const;
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, REndMemberReturnsInt const&>);
|
||||
|
||||
struct REndMemberReturnsVoidPtr {
|
||||
const void *rbegin() const;
|
||||
const void *rend() const;
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, REndMemberReturnsVoidPtr const&>);
|
||||
|
||||
struct PtrConvertible {
|
||||
operator int*() const;
|
||||
};
|
||||
struct PtrConvertibleREndMember {
|
||||
PtrConvertible rbegin() const;
|
||||
PtrConvertible rend() const;
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, PtrConvertibleREndMember const&>);
|
||||
|
||||
struct NoRBeginMember {
|
||||
constexpr const int* rend();
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, NoRBeginMember const&>);
|
||||
|
||||
struct NonConstREndMember {
|
||||
int x;
|
||||
constexpr int* rbegin() { return nullptr; }
|
||||
constexpr int* rend() { return &x; }
|
||||
};
|
||||
static_assert( std::is_invocable_v<RangeREndT, NonConstREndMember &>);
|
||||
static_assert(!std::is_invocable_v<RangeREndT, NonConstREndMember const&>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, NonConstREndMember &>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, NonConstREndMember const&>);
|
||||
|
||||
struct EnabledBorrowingREndMember {
|
||||
constexpr int* rbegin() const { return nullptr; }
|
||||
constexpr int* rend() const { return &globalBuff[0]; }
|
||||
};
|
||||
|
||||
template <>
|
||||
inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingREndMember> = true;
|
||||
|
||||
struct REndMemberFunction {
|
||||
int x;
|
||||
constexpr const int* rbegin() const { return nullptr; }
|
||||
constexpr const int* rend() const { return &x; }
|
||||
friend constexpr int* rend(REndMemberFunction const&);
|
||||
};
|
||||
|
||||
struct Empty { };
|
||||
struct EmptyEndMember {
|
||||
Empty rbegin() const;
|
||||
Empty rend() const;
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, EmptyEndMember const&>);
|
||||
|
||||
struct EmptyPtrREndMember {
|
||||
Empty x;
|
||||
constexpr const Empty* rbegin() const { return nullptr; }
|
||||
constexpr const Empty* rend() const { return &x; }
|
||||
};
|
||||
|
||||
constexpr bool testREndMember() {
|
||||
REndMember a;
|
||||
assert(std::ranges::rend(a) == &a.x);
|
||||
assert(std::ranges::crend(a) == &a.x);
|
||||
|
||||
NonConstREndMember b;
|
||||
assert(std::ranges::rend(b) == &b.x);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, decltype((b))>);
|
||||
|
||||
EnabledBorrowingREndMember c;
|
||||
assert(std::ranges::rend(std::move(c)) == &globalBuff[0]);
|
||||
assert(std::ranges::crend(std::move(c)) == &globalBuff[0]);
|
||||
|
||||
REndMemberFunction d;
|
||||
assert(std::ranges::rend(d) == &d.x);
|
||||
assert(std::ranges::crend(d) == &d.x);
|
||||
|
||||
EmptyPtrREndMember e;
|
||||
assert(std::ranges::rend(e) == &e.x);
|
||||
assert(std::ranges::crend(e) == &e.x);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct REndFunction {
|
||||
int x;
|
||||
friend constexpr const int* rbegin(REndFunction const&) { return nullptr; }
|
||||
friend constexpr const int* rend(REndFunction const& bf) { return &bf.x; }
|
||||
};
|
||||
|
||||
static_assert( std::is_invocable_v<RangeREndT, REndFunction const&>);
|
||||
static_assert(!std::is_invocable_v<RangeREndT, REndFunction &&>);
|
||||
|
||||
static_assert( std::is_invocable_v<RangeREndT, REndFunction const&>);
|
||||
static_assert(!std::is_invocable_v<RangeREndT, REndFunction &&>);
|
||||
static_assert(!std::is_invocable_v<RangeREndT, REndFunction &>);
|
||||
static_assert( std::is_invocable_v<RangeCREndT, REndFunction const&>);
|
||||
static_assert( std::is_invocable_v<RangeCREndT, REndFunction &>);
|
||||
|
||||
struct REndFunctionReturnsInt {
|
||||
friend constexpr int rbegin(REndFunctionReturnsInt const&);
|
||||
friend constexpr int rend(REndFunctionReturnsInt const&);
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, REndFunctionReturnsInt const&>);
|
||||
|
||||
struct REndFunctionReturnsVoidPtr {
|
||||
friend constexpr void* rbegin(REndFunctionReturnsVoidPtr const&);
|
||||
friend constexpr void* rend(REndFunctionReturnsVoidPtr const&);
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, REndFunctionReturnsVoidPtr const&>);
|
||||
|
||||
struct REndFunctionReturnsEmpty {
|
||||
friend constexpr Empty rbegin(REndFunctionReturnsEmpty const&);
|
||||
friend constexpr Empty rend(REndFunctionReturnsEmpty const&);
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, REndFunctionReturnsEmpty const&>);
|
||||
|
||||
struct REndFunctionReturnsPtrConvertible {
|
||||
friend constexpr PtrConvertible rbegin(REndFunctionReturnsPtrConvertible const&);
|
||||
friend constexpr PtrConvertible rend(REndFunctionReturnsPtrConvertible const&);
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, REndFunctionReturnsPtrConvertible const&>);
|
||||
|
||||
struct NoRBeginFunction {
|
||||
friend constexpr const int* rend(NoRBeginFunction const&);
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, NoRBeginFunction const&>);
|
||||
|
||||
struct REndFunctionByValue {
|
||||
friend constexpr int* rbegin(REndFunctionByValue) { return nullptr; }
|
||||
friend constexpr int* rend(REndFunctionByValue) { return &globalBuff[1]; }
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, REndFunctionByValue>);
|
||||
|
||||
struct REndFunctionEnabledBorrowing {
|
||||
friend constexpr int* rbegin(REndFunctionEnabledBorrowing) { return nullptr; }
|
||||
friend constexpr int* rend(REndFunctionEnabledBorrowing) { return &globalBuff[2]; }
|
||||
};
|
||||
template<>
|
||||
inline constexpr bool std::ranges::enable_borrowed_range<REndFunctionEnabledBorrowing> = true;
|
||||
|
||||
struct REndFunctionReturnsEmptyPtr {
|
||||
Empty x;
|
||||
friend constexpr const Empty* rbegin(REndFunctionReturnsEmptyPtr const&) { return nullptr; }
|
||||
friend constexpr const Empty* rend(REndFunctionReturnsEmptyPtr const& bf) { return &bf.x; }
|
||||
};
|
||||
|
||||
struct REndFunctionWithDataMember {
|
||||
int x;
|
||||
int rend;
|
||||
friend constexpr const int* rbegin(REndFunctionWithDataMember const&) { return nullptr; }
|
||||
friend constexpr const int* rend(REndFunctionWithDataMember const& bf) { return &bf.x; }
|
||||
};
|
||||
|
||||
struct REndFunctionWithPrivateEndMember : private REndMember {
|
||||
int y;
|
||||
friend constexpr const int* rbegin(REndFunctionWithPrivateEndMember const&) { return nullptr; }
|
||||
friend constexpr const int* rend(REndFunctionWithPrivateEndMember const& bf) { return &bf.y; }
|
||||
};
|
||||
|
||||
struct RBeginMemberEndFunction {
|
||||
int x;
|
||||
constexpr const int* rbegin() const { return nullptr; }
|
||||
friend constexpr const int* rend(RBeginMemberEndFunction const& bf) { return &bf.x; }
|
||||
};
|
||||
|
||||
constexpr bool testREndFunction() {
|
||||
const REndFunction a{};
|
||||
assert(std::ranges::rend(a) == &a.x);
|
||||
assert(std::ranges::crend(a) == &a.x);
|
||||
REndFunction aa{};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, decltype((aa))>);
|
||||
assert(std::ranges::crend(aa) == &aa.x);
|
||||
|
||||
REndFunctionByValue b;
|
||||
assert(std::ranges::rend(b) == &globalBuff[1]);
|
||||
assert(std::ranges::crend(b) == &globalBuff[1]);
|
||||
|
||||
REndFunctionEnabledBorrowing c;
|
||||
assert(std::ranges::rend(std::move(c)) == &globalBuff[2]);
|
||||
assert(std::ranges::crend(std::move(c)) == &globalBuff[2]);
|
||||
|
||||
const REndFunctionReturnsEmptyPtr d{};
|
||||
assert(std::ranges::rend(d) == &d.x);
|
||||
assert(std::ranges::crend(d) == &d.x);
|
||||
REndFunctionReturnsEmptyPtr dd{};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, decltype((dd))>);
|
||||
assert(std::ranges::crend(dd) == &dd.x);
|
||||
|
||||
const REndFunctionWithDataMember e{};
|
||||
assert(std::ranges::rend(e) == &e.x);
|
||||
assert(std::ranges::crend(e) == &e.x);
|
||||
REndFunctionWithDataMember ee{};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, decltype((ee))>);
|
||||
assert(std::ranges::crend(ee) == &ee.x);
|
||||
|
||||
const REndFunctionWithPrivateEndMember f{};
|
||||
assert(std::ranges::rend(f) == &f.y);
|
||||
assert(std::ranges::crend(f) == &f.y);
|
||||
REndFunctionWithPrivateEndMember ff{};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, decltype((ff))>);
|
||||
assert(std::ranges::crend(ff) == &ff.y);
|
||||
|
||||
const RBeginMemberEndFunction g{};
|
||||
assert(std::ranges::rend(g) == &g.x);
|
||||
assert(std::ranges::crend(g) == &g.x);
|
||||
RBeginMemberEndFunction gg{};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, decltype((gg))>);
|
||||
assert(std::ranges::crend(gg) == &gg.x);
|
||||
|
||||
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<RangeREndT, MemberBeginEnd&>);
|
||||
static_assert( std::is_invocable_v<RangeREndT, MemberBeginEnd const&>);
|
||||
static_assert( std::is_invocable_v<RangeCREndT, 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<RangeREndT, FunctionBeginEnd&>);
|
||||
static_assert( std::is_invocable_v<RangeREndT, FunctionBeginEnd const&>);
|
||||
static_assert( std::is_invocable_v<RangeCREndT, 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<RangeREndT, MemberBeginFunctionEnd&>);
|
||||
static_assert( std::is_invocable_v<RangeREndT, MemberBeginFunctionEnd const&>);
|
||||
static_assert( std::is_invocable_v<RangeCREndT, 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<RangeREndT, FunctionBeginMemberEnd&>);
|
||||
static_assert( std::is_invocable_v<RangeREndT, FunctionBeginMemberEnd const&>);
|
||||
static_assert( std::is_invocable_v<RangeCREndT, FunctionBeginMemberEnd const&>);
|
||||
|
||||
struct MemberBeginEndDifferentTypes {
|
||||
bidirectional_iterator<int*> begin();
|
||||
bidirectional_iterator<const int*> end();
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, MemberBeginEndDifferentTypes&>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginEndDifferentTypes&>);
|
||||
|
||||
struct FunctionBeginEndDifferentTypes {
|
||||
friend bidirectional_iterator<int*> begin(FunctionBeginEndDifferentTypes&);
|
||||
friend bidirectional_iterator<const int*> end(FunctionBeginEndDifferentTypes&);
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginEndDifferentTypes&>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginEndDifferentTypes&>);
|
||||
|
||||
struct MemberBeginEndForwardIterators {
|
||||
forward_iterator<int*> begin();
|
||||
forward_iterator<int*> end();
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, MemberBeginEndForwardIterators&>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginEndForwardIterators&>);
|
||||
|
||||
struct FunctionBeginEndForwardIterators {
|
||||
friend forward_iterator<int*> begin(FunctionBeginEndForwardIterators&);
|
||||
friend forward_iterator<int*> end(FunctionBeginEndForwardIterators&);
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginEndForwardIterators&>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginEndForwardIterators&>);
|
||||
|
||||
struct MemberBeginOnly {
|
||||
bidirectional_iterator<int*> begin() const;
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, MemberBeginOnly&>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginOnly&>);
|
||||
|
||||
struct FunctionBeginOnly {
|
||||
friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginOnly&>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginOnly&>);
|
||||
|
||||
struct MemberEndOnly {
|
||||
bidirectional_iterator<int*> end() const;
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, MemberEndOnly&>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, MemberEndOnly&>);
|
||||
|
||||
struct FunctionEndOnly {
|
||||
friend bidirectional_iterator<int*> end(FunctionEndOnly&);
|
||||
};
|
||||
static_assert(!std::is_invocable_v<RangeREndT, FunctionEndOnly&>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, 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<RangeREndT, MemberBeginAndRBegin&>);
|
||||
static_assert( std::is_invocable_v<RangeCREndT, MemberBeginAndRBegin&>);
|
||||
static_assert( std::same_as<std::invoke_result_t<RangeREndT, MemberBeginAndRBegin&>, int*>);
|
||||
static_assert( std::same_as<std::invoke_result_t<RangeCREndT, MemberBeginAndRBegin&>, int*>);
|
||||
|
||||
constexpr bool testBeginEnd() {
|
||||
MemberBeginEnd a{};
|
||||
const MemberBeginEnd aa{};
|
||||
assert(std::ranges::rend(a).base().base() == &a.b);
|
||||
assert(std::ranges::crend(a).base().base() == &a.cb);
|
||||
assert(std::ranges::rend(aa).base().base() == &aa.cb);
|
||||
assert(std::ranges::crend(aa).base().base() == &aa.cb);
|
||||
|
||||
FunctionBeginEnd b{};
|
||||
const FunctionBeginEnd bb{};
|
||||
assert(std::ranges::rend(b).base().base() == &b.b);
|
||||
assert(std::ranges::crend(b).base().base() == &b.cb);
|
||||
assert(std::ranges::rend(bb).base().base() == &bb.cb);
|
||||
assert(std::ranges::crend(bb).base().base() == &bb.cb);
|
||||
|
||||
MemberBeginFunctionEnd c{};
|
||||
const MemberBeginFunctionEnd cc{};
|
||||
assert(std::ranges::rend(c).base().base() == &c.b);
|
||||
assert(std::ranges::crend(c).base().base() == &c.cb);
|
||||
assert(std::ranges::rend(cc).base().base() == &cc.cb);
|
||||
assert(std::ranges::crend(cc).base().base() == &cc.cb);
|
||||
|
||||
FunctionBeginMemberEnd d{};
|
||||
const FunctionBeginMemberEnd dd{};
|
||||
assert(std::ranges::rend(d).base().base() == &d.b);
|
||||
assert(std::ranges::crend(d).base().base() == &d.cb);
|
||||
assert(std::ranges::rend(dd).base().base() == &dd.cb);
|
||||
assert(std::ranges::crend(dd).base().base() == &dd.cb);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ASSERT_NOEXCEPT(std::ranges::rend(std::declval<int (&)[10]>()));
|
||||
ASSERT_NOEXCEPT(std::ranges::crend(std::declval<int (&)[10]>()));
|
||||
|
||||
struct NoThrowMemberREnd {
|
||||
ThrowingIterator<int> rbegin() const;
|
||||
ThrowingIterator<int> rend() const noexcept; // auto(t.rend()) doesn't throw
|
||||
} ntmre;
|
||||
static_assert(noexcept(std::ranges::rend(ntmre)));
|
||||
static_assert(noexcept(std::ranges::crend(ntmre)));
|
||||
|
||||
struct NoThrowADLREnd {
|
||||
ThrowingIterator<int> rbegin() const;
|
||||
friend ThrowingIterator<int> rend(NoThrowADLREnd&) noexcept; // auto(rend(t)) doesn't throw
|
||||
friend ThrowingIterator<int> rend(const NoThrowADLREnd&) noexcept;
|
||||
} ntare;
|
||||
static_assert(noexcept(std::ranges::rend(ntare)));
|
||||
static_assert(noexcept(std::ranges::crend(ntare)));
|
||||
|
||||
struct NoThrowMemberREndReturnsRef {
|
||||
ThrowingIterator<int> rbegin() const;
|
||||
ThrowingIterator<int>& rend() const noexcept; // auto(t.rend()) may throw
|
||||
} ntmrerr;
|
||||
static_assert(!noexcept(std::ranges::rend(ntmrerr)));
|
||||
static_assert(!noexcept(std::ranges::crend(ntmrerr)));
|
||||
|
||||
struct REndReturnsArrayRef {
|
||||
auto rbegin() const noexcept -> int(&)[10];
|
||||
auto rend() const noexcept -> int(&)[10];
|
||||
} rerar;
|
||||
static_assert(noexcept(std::ranges::rend(rerar)));
|
||||
static_assert(noexcept(std::ranges::crend(rerar)));
|
||||
|
||||
struct NoThrowBeginThrowingEnd {
|
||||
int* begin() const noexcept;
|
||||
int* end() const;
|
||||
} ntbte;
|
||||
static_assert(noexcept(std::ranges::rend(ntbte)));
|
||||
static_assert(noexcept(std::ranges::crend(ntbte)));
|
||||
|
||||
struct NoThrowEndThrowingBegin {
|
||||
int* begin() const;
|
||||
int* end() const noexcept;
|
||||
} ntetb;
|
||||
static_assert(!noexcept(std::ranges::rend(ntetb)));
|
||||
static_assert(!noexcept(std::ranges::crend(ntetb)));
|
||||
|
||||
// Test ADL-proofing.
|
||||
struct Incomplete;
|
||||
template<class T> struct Holder { T t; };
|
||||
static_assert(!std::is_invocable_v<RangeREndT, Holder<Incomplete>*>);
|
||||
static_assert(!std::is_invocable_v<RangeREndT, Holder<Incomplete>*&>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*>);
|
||||
static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*&>);
|
||||
|
||||
int main(int, char**) {
|
||||
static_assert(testReturnTypes());
|
||||
|
||||
testArray();
|
||||
static_assert(testArray());
|
||||
|
||||
testREndMember();
|
||||
static_assert(testREndMember());
|
||||
|
||||
testREndFunction();
|
||||
static_assert(testREndFunction());
|
||||
|
||||
testBeginEnd();
|
||||
static_assert(testBeginEnd());
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user