Skip to content

Commit cf09b7d

Browse files
author
Xiaoyang Liu
authored
[libc++][ranges] LWG3715: view_interface::empty is overconstrained (#85004)
## Abstract This pull request implements LWG3715: `view_interface::empty` is overconstrained. Here is an example similar to those described in the report, which compiles with `-stdlib=libstdc++` but failed to compile with `-stdlib=libc++`: ```cpp // https://godbolt.org/z/EWEoTzah3 std::istringstream input("1 2 3 4 5"); auto i = std::views::istream<int>(input); auto r = std::views::counted(i.begin(), 4) | std::views::take(2); assert(!r.empty()); ``` ## Reference - [Draft C++ Standard: [view.interface.general]](https://eel.is/c++draft/view.interface.general) - [LWG3715](https://wg21.link/LWG3715)
1 parent 272d1b4 commit cf09b7d

File tree

3 files changed

+35
-5
lines changed

3 files changed

+35
-5
lines changed

libcxx/docs/Status/Cxx23Issues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@
181181
"`3711 <https://wg21.link/LWG3711>`__","Missing preconditions for slide_view constructor","July 2022","","","|ranges|"
182182
"`3712 <https://wg21.link/LWG3712>`__","``chunk_view`` and ``slide_view`` should not be ``default_initializable``","July 2022","","","|ranges|"
183183
"`3713 <https://wg21.link/LWG3713>`__","Sorted with respect to comparator (only)","July 2022","",""
184-
"`3715 <https://wg21.link/LWG3715>`__","``view_interface::empty`` is overconstrained","July 2022","","","|ranges|"
184+
"`3715 <https://wg21.link/LWG3715>`__","``view_interface::empty`` is overconstrained","July 2022","|Complete|","19.0","|ranges|"
185185
"`3719 <https://wg21.link/LWG3719>`__","Directory iterators should be usable with default sentinel","July 2022","|Complete|","17.0","|ranges|"
186186
"`3721 <https://wg21.link/LWG3721>`__","Allow an ``arg-id`` with a value of zero for ``width`` in ``std-format-spec``","July 2022","|Complete|","16.0","|format|"
187187
"`3724 <https://wg21.link/LWG3724>`__","``decay-copy`` should be constrained","July 2022","|Complete|","14.0"

libcxx/include/__ranges/view_interface.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <__ranges/access.h>
2222
#include <__ranges/concepts.h>
2323
#include <__ranges/empty.h>
24+
#include <__ranges/size.h>
2425
#include <__type_traits/is_class.h>
2526
#include <__type_traits/make_unsigned.h>
2627
#include <__type_traits/remove_cv.h>
@@ -51,16 +52,24 @@ class view_interface {
5152
public:
5253
template <class _D2 = _Derived>
5354
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool empty()
54-
requires forward_range<_D2>
55+
requires sized_range<_D2> || forward_range<_D2>
5556
{
56-
return ranges::begin(__derived()) == ranges::end(__derived());
57+
if constexpr (sized_range<_D2>) {
58+
return ranges::size(__derived()) == 0;
59+
} else {
60+
return ranges::begin(__derived()) == ranges::end(__derived());
61+
}
5762
}
5863

5964
template <class _D2 = _Derived>
6065
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const
61-
requires forward_range<const _D2>
66+
requires sized_range<const _D2> || forward_range<const _D2>
6267
{
63-
return ranges::begin(__derived()) == ranges::end(__derived());
68+
if constexpr (sized_range<const _D2>) {
69+
return ranges::size(__derived()) == 0;
70+
} else {
71+
return ranges::begin(__derived()) == ranges::end(__derived());
72+
}
6473
}
6574

6675
template <class _D2 = _Derived>

libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ struct InputRange : std::ranges::view_interface<InputRange> {
3939
constexpr InputIter end() const { return InputIter(buff + 8); }
4040
};
4141

42+
struct SizedInputRange : std::ranges::view_interface<SizedInputRange> {
43+
int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
44+
constexpr InputIter begin() const { return InputIter(buff); }
45+
constexpr sentinel_wrapper<InputIter> end() const { return sentinel_wrapper(InputIter(buff + 8)); }
46+
constexpr std::size_t size() const { return 8; }
47+
};
48+
static_assert(std::ranges::sized_range<SizedInputRange>);
49+
4250
struct NotSizedSentinel {
4351
using value_type = int;
4452
using difference_type = std::ptrdiff_t;
@@ -155,11 +163,24 @@ concept BoolOpInvocable = requires (T const& obj) { bool(obj); };
155163

156164
constexpr bool testEmpty() {
157165
static_assert(!EmptyInvocable<InputRange>);
166+
// LWG 3715: `view_interface::empty` is overconstrained
167+
static_assert(EmptyInvocable<SizedInputRange>);
158168
static_assert( EmptyInvocable<ForwardRange>);
159169

160170
static_assert(!BoolOpInvocable<InputRange>);
171+
static_assert(BoolOpInvocable<SizedInputRange>);
161172
static_assert( BoolOpInvocable<ForwardRange>);
162173

174+
SizedInputRange sizedInputRange;
175+
assert(!sizedInputRange.empty());
176+
assert(!static_cast<SizedInputRange const&>(sizedInputRange).empty());
177+
178+
assert(sizedInputRange);
179+
assert(static_cast<SizedInputRange const&>(sizedInputRange));
180+
181+
assert(!std::ranges::empty(sizedInputRange));
182+
assert(!std::ranges::empty(static_cast<SizedInputRange const&>(sizedInputRange)));
183+
163184
ForwardRange forwardRange;
164185
assert(!forwardRange.empty());
165186
assert(!static_cast<ForwardRange const&>(forwardRange).empty());

0 commit comments

Comments
 (0)