Skip to content

Commit c8f3d21

Browse files
authored
[ADT] Allow reverse to find free rbegin/rend functions (#87840)
Lift the requirement that rbegin/rend must be member functions. Also allow the rbegin/rend to be found through Argument Dependent Lookup (ADL) and add `adl_rbegin`/`adl_rend` to STLExtras.
1 parent 0f52f4d commit c8f3d21

File tree

5 files changed

+69
-25
lines changed

5 files changed

+69
-25
lines changed

llvm/include/llvm/ADT/ADL.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,22 @@ constexpr auto end_impl(RangeT &&range)
3737
return end(std::forward<RangeT>(range));
3838
}
3939

40+
using std::rbegin;
41+
42+
template <typename RangeT>
43+
constexpr auto rbegin_impl(RangeT &&range)
44+
-> decltype(rbegin(std::forward<RangeT>(range))) {
45+
return rbegin(std::forward<RangeT>(range));
46+
}
47+
48+
using std::rend;
49+
50+
template <typename RangeT>
51+
constexpr auto rend_impl(RangeT &&range)
52+
-> decltype(rend(std::forward<RangeT>(range))) {
53+
return rend(std::forward<RangeT>(range));
54+
}
55+
4056
using std::swap;
4157

4258
template <typename T>
@@ -72,6 +88,22 @@ constexpr auto adl_end(RangeT &&range)
7288
return adl_detail::end_impl(std::forward<RangeT>(range));
7389
}
7490

91+
/// Returns the reverse-begin iterator to \p range using `std::rbegin` and
92+
/// function found through Argument-Dependent Lookup (ADL).
93+
template <typename RangeT>
94+
constexpr auto adl_rbegin(RangeT &&range)
95+
-> decltype(adl_detail::rbegin_impl(std::forward<RangeT>(range))) {
96+
return adl_detail::rbegin_impl(std::forward<RangeT>(range));
97+
}
98+
99+
/// Returns the reverse-end iterator to \p range using `std::rend` and
100+
/// functions found through Argument-Dependent Lookup (ADL).
101+
template <typename RangeT>
102+
constexpr auto adl_rend(RangeT &&range)
103+
-> decltype(adl_detail::rend_impl(std::forward<RangeT>(range))) {
104+
return adl_detail::rend_impl(std::forward<RangeT>(range));
105+
}
106+
75107
/// Swaps \p lhs with \p rhs using `std::swap` and functions found through
76108
/// Argument-Dependent Lookup (ADL).
77109
template <typename T>

llvm/include/llvm/ADT/STLExtras.h

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -405,32 +405,23 @@ class mapped_iterator_base
405405
}
406406
};
407407

408-
/// Helper to determine if type T has a member called rbegin().
409-
template <typename Ty> class has_rbegin_impl {
410-
using yes = char[1];
411-
using no = char[2];
412-
413-
template <typename Inner>
414-
static yes& test(Inner *I, decltype(I->rbegin()) * = nullptr);
415-
416-
template <typename>
417-
static no& test(...);
418-
419-
public:
420-
static const bool value = sizeof(test<Ty>(nullptr)) == sizeof(yes);
421-
};
408+
namespace detail {
409+
template <typename Range>
410+
using check_has_free_function_rbegin =
411+
decltype(adl_rbegin(std::declval<Range &>()));
422412

423-
/// Metafunction to determine if T& or T has a member called rbegin().
424-
template <typename Ty>
425-
struct has_rbegin : has_rbegin_impl<std::remove_reference_t<Ty>> {};
413+
template <typename Range>
414+
static constexpr bool HasFreeFunctionRBegin =
415+
is_detected<check_has_free_function_rbegin, Range>::value;
416+
} // namespace detail
426417

427418
// Returns an iterator_range over the given container which iterates in reverse.
428419
template <typename ContainerTy> auto reverse(ContainerTy &&C) {
429-
if constexpr (has_rbegin<ContainerTy>::value)
430-
return make_range(C.rbegin(), C.rend());
420+
if constexpr (detail::HasFreeFunctionRBegin<ContainerTy>)
421+
return make_range(adl_rbegin(C), adl_rend(C));
431422
else
432-
return make_range(std::make_reverse_iterator(std::end(C)),
433-
std::make_reverse_iterator(std::begin(C)));
423+
return make_range(std::make_reverse_iterator(adl_end(C)),
424+
std::make_reverse_iterator(adl_begin(C)));
434425
}
435426

436427
/// An iterator adaptor that filters the elements of given inner iterators.

llvm/unittests/ADT/IteratorTest.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,21 @@ TEST(PointerIterator, Range) {
395395
EXPECT_EQ(A + I++, P);
396396
}
397397

398+
namespace rbegin_detail {
399+
struct WithFreeRBegin {
400+
int data[3] = {42, 43, 44};
401+
};
402+
403+
auto rbegin(const WithFreeRBegin &X) { return std::rbegin(X.data); }
404+
auto rend(const WithFreeRBegin &X) { return std::rend(X.data); }
405+
} // namespace rbegin_detail
406+
407+
TEST(ReverseTest, ADL) {
408+
// Check that we can find the rbegin/rend functions via ADL.
409+
rbegin_detail::WithFreeRBegin Foo;
410+
EXPECT_THAT(reverse(Foo), ElementsAre(44, 43, 42));
411+
}
412+
398413
TEST(ZipIteratorTest, Basic) {
399414
using namespace std;
400415
const SmallVector<unsigned, 6> pi{3, 1, 4, 1, 5, 9};

llvm/unittests/ADT/RangeAdapterTest.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,6 @@ TYPED_TEST(RangeAdapterRValueTest, TrivialOperation) {
150150
TestRev(reverse(TypeParam({0, 1, 2, 3})));
151151
}
152152

153-
TYPED_TEST(RangeAdapterRValueTest, HasRbegin) {
154-
static_assert(has_rbegin<TypeParam>::value, "rbegin() should be defined");
155-
}
156-
157153
TYPED_TEST(RangeAdapterRValueTest, RangeType) {
158154
static_assert(
159155
std::is_same_v<decltype(reverse(std::declval<TypeParam>()).begin()),

llvm/unittests/ADT/STLExtrasTest.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,14 @@ std::vector<int>::const_iterator end(const some_struct &s) {
405405
return s.data.end();
406406
}
407407

408+
std::vector<int>::const_reverse_iterator rbegin(const some_struct &s) {
409+
return s.data.rbegin();
410+
}
411+
412+
std::vector<int>::const_reverse_iterator rend(const some_struct &s) {
413+
return s.data.rend();
414+
}
415+
408416
void swap(some_struct &lhs, some_struct &rhs) {
409417
// make swap visible as non-adl swap would even seem to
410418
// work with std::swap which defaults to moving
@@ -573,6 +581,8 @@ TEST(STLExtrasTest, ADLTest) {
573581

574582
EXPECT_EQ(*adl_begin(s), 1);
575583
EXPECT_EQ(*(adl_end(s) - 1), 5);
584+
EXPECT_EQ(*adl_rbegin(s), 5);
585+
EXPECT_EQ(*(adl_rend(s) - 1), 1);
576586

577587
adl_swap(s, s2);
578588
EXPECT_EQ(s.swap_val, "lhs");

0 commit comments

Comments
 (0)