[libcxx-commits] [libcxx] 8200e12 - [libc++][ranges] Implement `views::drop`.
via libcxx-commits
libcxx-commits at lists.llvm.org
Tue May 10 09:31:37 PDT 2022
Author: Konstantin Varlamov
Date: 2022-05-10T09:31:05-07:00
New Revision: 8200e1253f6f300dc70b3457589e99a5a618f2d7
URL: https://github.com/llvm/llvm-project/commit/8200e1253f6f300dc70b3457589e99a5a618f2d7
DIFF: https://github.com/llvm/llvm-project/commit/8200e1253f6f300dc70b3457589e99a5a618f2d7.diff
LOG: [libc++][ranges] Implement `views::drop`.
The view itself has been implemented previously -- this patch only adds
the ability to pipe it.
Also finishes the implementation of [P1739](https://wg21.link/p1739) and
[LWG3407](https://wg21.link/lwg3407).
Differential Revision: https://reviews.llvm.org/D125156
Added:
libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp
Modified:
libcxx/docs/Status/Cxx20Papers.csv
libcxx/docs/Status/Cxx2bIssues.csv
libcxx/docs/Status/RangesIssues.csv
libcxx/include/__ranges/drop_view.h
libcxx/include/__ranges/subrange.h
libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 65e4014da15a4..aa2d567196716 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -168,7 +168,7 @@
"`P1115R3 <https://wg21.link/P1115R3>`__","LWG","Improving the Return Value of Erase-Like Algorithms II: Free erase/erase if","Prague","|Complete|","11.0"
"`P1243R4 <https://wg21.link/P1243R4>`__","LWG","Rangify New Algorithms","Prague","* *",""
"`P1460R1 <https://wg21.link/P1460R1>`__","LWG","Mandating the Standard Library: Clause 20 - Utilities library","Prague","* *",""
-"`P1739R4 <https://wg21.link/P1739R4>`__","LWG","Avoid template bloat for safe_ranges in combination with ""subrange-y"" view adaptors","Prague","* *",""
+"`P1739R4 <https://wg21.link/P1739R4>`__","LWG","Avoid template bloat for safe_ranges in combination with ""subrange-y"" view adaptors","Prague","|Complete|","15.0"
"`P1831R1 <https://wg21.link/P1831R1>`__","LWG","Deprecating volatile: library","Prague","* *",""
"`P1868R2 <https://wg21.link/P1868R2>`__","LWG","width: clarifying units of width and precision in std::format","Prague","|Complete|","14.0"
"`P1908R1 <https://wg21.link/P1908R1>`__","CWG","Reserving Attribute Namespaces for Future Use","Prague","* *",""
diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv
index 9047a63b6dd65..0391f64f05df7 100644
--- a/libcxx/docs/Status/Cxx2bIssues.csv
+++ b/libcxx/docs/Status/Cxx2bIssues.csv
@@ -107,7 +107,7 @@
`3293 <https://wg21.link/LWG3293>`__,"``move_iterator operator+()`` has incorrect constraints","October 2021","|Complete|","15.0","|ranges|"
`3361 <https://wg21.link/LWG3361>`__,"``safe_range<SomeRange&>`` case","October 2021","|Nothing To Do|","","|ranges|"
`3392 <https://wg21.link/LWG3392>`__,"``ranges::distance()`` cannot be used on a move-only iterator with a sized sentinel","October 2021","|Complete|","14.0","|ranges|"
-`3407 <https://wg21.link/LWG3407>`__,"Some problems with the wording changes of P1739R4","October 2021","","","|ranges|"
+`3407 <https://wg21.link/LWG3407>`__,"Some problems with the wording changes of P1739R4","October 2021","|Complete|","15.0","|ranges|"
`3422 <https://wg21.link/LWG3422>`__,"Issues of ``seed_seq``'s constructors","October 2021","|Complete|","14.0"
`3470 <https://wg21.link/LWG3470>`__,"``convertible-to-non-slicing`` seems to reject valid case","October 2021","|Complete|","14.0","|ranges|"
`3480 <https://wg21.link/LWG3480>`__,"``directory_iterator`` and ``recursive_directory_iterator`` are not C++20 ranges","October 2021","|Complete|","14.0","|ranges|"
diff --git a/libcxx/docs/Status/RangesIssues.csv b/libcxx/docs/Status/RangesIssues.csv
index 12d40e32fa734..4e0adbea268c0 100644
--- a/libcxx/docs/Status/RangesIssues.csv
+++ b/libcxx/docs/Status/RangesIssues.csv
@@ -12,7 +12,7 @@
`P1523R1 <https://wg21.link/P1523R1>`__,Views And Size Types,,
`P1638R1 <https://wg21.link/P1638R1>`__,basic_istream_view::iterator Should Not Be Copyable,,
`P1716R3 <https://wg21.link/P1716R3>`__,Range Comparison Algorithms Are Over-Constrained,,
-`P1739R4 <https://wg21.link/P1739R4>`__,Avoiding Template Bloat For Ranges,,
+`P1739R4 <https://wg21.link/P1739R4>`__,Avoiding Template Bloat For Ranges,|Complete|,15.0
`P1862R1 <https://wg21.link/P1862R1>`__,Range Adaptors For Non-Copyable Iterators,,
`P1870R1 <https://wg21.link/P1870R1>`__,safe_range,,
`P1871R1 <https://wg21.link/P1871R1>`__,disable_sized_sentinel_for,|Complete|,14.0
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 93f4b9f188a7c..962bc4e900db1 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -9,18 +9,29 @@
#ifndef _LIBCPP___RANGES_DROP_VIEW_H
#define _LIBCPP___RANGES_DROP_VIEW_H
+#include <__algorithm/min.h>
#include <__assert>
#include <__config>
+#include <__functional/bind_back.h>
+#include <__fwd/span.h>
+#include <__fwd/string_view.h>
#include <__iterator/concepts.h>
+#include <__iterator/distance.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/next.h>
#include <__ranges/access.h>
#include <__ranges/all.h>
#include <__ranges/concepts.h>
+#include <__ranges/empty_view.h>
#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/iota_view.h>
#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
#include <__ranges/size.h>
+#include <__ranges/subrange.h>
#include <__ranges/view_interface.h>
+#include <__utility/auto_cast.h>
+#include <__utility/forward.h>
#include <__utility/move.h>
#include <concepts>
#include <type_traits>
@@ -113,11 +124,174 @@ namespace ranges {
{ return __size(*this); }
};
- template<class _Range>
- drop_view(_Range&&, range_
diff erence_t<_Range>) -> drop_view<views::all_t<_Range>>;
+template<class _Range>
+drop_view(_Range&&, range_
diff erence_t<_Range>) -> drop_view<views::all_t<_Range>>;
+
+template<class _Tp>
+inline constexpr bool enable_borrowed_range<drop_view<_Tp>> = enable_borrowed_range<_Tp>;
+
+namespace views {
+namespace __drop {
+
+template <class _Tp>
+inline constexpr bool __is_empty_view = false;
+
+template <class _Tp>
+inline constexpr bool __is_empty_view<empty_view<_Tp>> = true;
+
+template <class _Tp>
+inline constexpr bool __is_passthrough_specialization = false;
+
+template <class _Tp, size_t _Extent>
+inline constexpr bool __is_passthrough_specialization<span<_Tp, _Extent>> = true;
+
+template <class _CharT, class _Traits>
+inline constexpr bool __is_passthrough_specialization<basic_string_view<_CharT, _Traits>> = true;
+
+template <class _Np, class _Bound>
+inline constexpr bool __is_passthrough_specialization<iota_view<_Np, _Bound>> = true;
+
+template <class _Iter, class _Sent, subrange_kind _Kind>
+inline constexpr bool __is_passthrough_specialization<subrange<_Iter, _Sent, _Kind>> =
+ !subrange<_Iter, _Sent, _Kind>::_StoreSize;
+
+template <class _Tp>
+inline constexpr bool __is_subrange_specialization_with_store_size = false;
+
+template <class _Iter, class _Sent, subrange_kind _Kind>
+inline constexpr bool __is_subrange_specialization_with_store_size<subrange<_Iter, _Sent, _Kind>> =
+ subrange<_Iter, _Sent, _Kind>::_StoreSize;
+
+template <class _Tp>
+struct __passthrough_type;
+
+template <class _Tp, size_t _Extent>
+struct __passthrough_type<span<_Tp, _Extent>> {
+ using type = span<_Tp>;
+};
+
+template <class _CharT, class _Traits>
+struct __passthrough_type<basic_string_view<_CharT, _Traits>> {
+ using type = basic_string_view<_CharT, _Traits>;
+};
+
+template <class _Np, class _Bound>
+struct __passthrough_type<iota_view<_Np, _Bound>> {
+ using type = iota_view<_Np, _Bound>;
+};
+
+template <class _Iter, class _Sent, subrange_kind _Kind>
+struct __passthrough_type<subrange<_Iter, _Sent, _Kind>> {
+ using type = subrange<_Iter, _Sent, _Kind>;
+};
+
+template <class _Tp>
+using __passthrough_type_t = typename __passthrough_type<_Tp>::type;
+
+struct __fn {
+ // [range.drop.overview]: the `empty_view` case.
+ template <class _Range, convertible_to<range_
diff erence_t<_Range>> _Np>
+ requires __is_empty_view<remove_cvref_t<_Range>>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Range&& __range, _Np&&) const
+ noexcept(noexcept(_LIBCPP_AUTO_CAST(std::forward<_Range>(__range))))
+ -> decltype( _LIBCPP_AUTO_CAST(std::forward<_Range>(__range)))
+ { return _LIBCPP_AUTO_CAST(std::forward<_Range>(__range)); }
+
+ // [range.drop.overview]: the `span | basic_string_view | iota_view | subrange (StoreSize == false)` case.
+ template <class _Range,
+ convertible_to<range_
diff erence_t<_Range>> _Np,
+ class _RawRange = remove_cvref_t<_Range>,
+ class _Dist = range_
diff erence_t<_Range>>
+ requires (!__is_empty_view<_RawRange> &&
+ random_access_range<_RawRange> &&
+ sized_range<_RawRange> &&
+ __is_passthrough_specialization<_RawRange>)
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Range&& __rng, _Np&& __n) const
+ noexcept(noexcept(__passthrough_type_t<_RawRange>(
+ ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)),
+ ranges::end(__rng)
+ )))
+ -> decltype( __passthrough_type_t<_RawRange>(
+ // Note: deliberately not forwarding `__rng` to guard against double moves.
+ ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)),
+ ranges::end(__rng)
+ ))
+ { return __passthrough_type_t<_RawRange>(
+ ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)),
+ ranges::end(__rng)
+ ); }
+
+ // [range.drop.overview]: the `subrange (StoreSize == true)` case.
+ template <class _Range,
+ convertible_to<range_
diff erence_t<_Range>> _Np,
+ class _RawRange = remove_cvref_t<_Range>,
+ class _Dist = range_
diff erence_t<_Range>>
+ requires (!__is_empty_view<_RawRange> &&
+ random_access_range<_RawRange> &&
+ sized_range<_RawRange> &&
+ __is_subrange_specialization_with_store_size<_RawRange>)
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Range&& __rng, _Np&& __n) const
+ noexcept(noexcept(_RawRange(
+ ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)),
+ ranges::end(__rng),
+ std::__to_unsigned_like(ranges::distance(__rng) -
+ std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)))
+ )))
+ -> decltype( _RawRange(
+ // Note: deliberately not forwarding `__rng` to guard against double moves.
+ ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)),
+ ranges::end(__rng),
+ std::__to_unsigned_like(ranges::distance(__rng) -
+ std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)))
+ ))
+ {
+ // Introducing local variables avoids calculating `min` and `distance` twice (at the cost of diverging from the
+ // expression used in the `noexcept` clause and the return statement).
+ auto dist = ranges::distance(__rng);
+ auto clamped = std::min<_Dist>(dist, std::forward<_Np>(__n));
+ return _RawRange(
+ ranges::begin(__rng) + clamped,
+ ranges::end(__rng),
+ std::__to_unsigned_like(dist - clamped)
+ );}
+
+ // [range.drop.overview]: the "otherwise" case.
+ template <class _Range, convertible_to<range_
diff erence_t<_Range>> _Np,
+ class _RawRange = remove_cvref_t<_Range>>
+ // Note: without specifically excluding the other cases, GCC sees this overload as ambiguous with the other
+ // overloads.
+ requires (!(__is_empty_view<_RawRange> ||
+ (__is_subrange_specialization_with_store_size<_RawRange> &&
+ sized_range<_RawRange> &&
+ random_access_range<_RawRange>) ||
+ (__is_passthrough_specialization<_RawRange> &&
+ sized_range<_RawRange> &&
+ random_access_range<_RawRange>)
+ ))
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Range&& __range, _Np&& __n) const
+ noexcept(noexcept(drop_view(std::forward<_Range>(__range), std::forward<_Np>(__n))))
+ -> decltype( drop_view(std::forward<_Range>(__range), std::forward<_Np>(__n)))
+ { return drop_view(std::forward<_Range>(__range), std::forward<_Np>(__n)); }
+
+ template <class _Np>
+ requires constructible_from<decay_t<_Np>, _Np>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Np&& __n) const
+ noexcept(is_nothrow_constructible_v<decay_t<_Np>, _Np>)
+ { return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Np>(__n))); }
+};
+
+} // namespace __drop
+
+inline namespace __cpo {
+ inline constexpr auto drop = __drop::__fn{};
+} // namespace __cpo
+} // namespace views
- template<class _Tp>
- inline constexpr bool enable_borrowed_range<drop_view<_Tp>> = enable_borrowed_range<_Tp>;
} // namespace ranges
#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
diff --git a/libcxx/include/__ranges/subrange.h b/libcxx/include/__ranges/subrange.h
index 24e5eff3f8a49..7e42da6ce7f99 100644
--- a/libcxx/include/__ranges/subrange.h
+++ b/libcxx/include/__ranges/subrange.h
@@ -77,8 +77,11 @@ namespace ranges {
class _LIBCPP_TEMPLATE_VIS subrange
: public view_interface<subrange<_Iter, _Sent, _Kind>>
{
- private:
+ public:
+ // Note: this is an internal implementation detail that is public only for internal usage.
static constexpr bool _StoreSize = (_Kind == subrange_kind::sized && !sized_sentinel_for<_Sent, _Iter>);
+
+ private:
static constexpr bool _MustProvideSizeAtConstruction = !_StoreSize; // just to improve compiler diagnostics
struct _Empty { constexpr _Empty(auto) noexcept { } };
using _Size = conditional_t<_StoreSize, make_unsigned_t<iter_
diff erence_t<_Iter>>, _Empty>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp
new file mode 100644
index 0000000000000..4d97aed21dc45
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp
@@ -0,0 +1,239 @@
+//===----------------------------------------------------------------------===//
+//
+// 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-has-no-incomplete-ranges
+
+// std::views::drop
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <span>
+#include <string_view>
+#include <utility>
+#include "test_iterators.h"
+
+template <class View, class T>
+concept CanBePiped = requires (View&& view, T&& t) {
+ { std::forward<View>(view) | std::forward<T>(t) };
+};
+
+struct SizedView : std::ranges::view_base {
+ int* begin_ = nullptr;
+ int* end_ = nullptr;
+ constexpr SizedView(int* begin, int* end) : begin_(begin), end_(end) {}
+
+ constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+ constexpr auto end() const { return sized_sentinel<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+};
+static_assert(std::ranges::forward_range<SizedView>);
+static_assert(std::ranges::sized_range<SizedView>);
+static_assert(std::ranges::view<SizedView>);
+
+struct SizedViewWithUnsizedSentinel : std::ranges::view_base {
+ using iterator = random_access_iterator<int*>;
+ using sentinel = sentinel_wrapper<random_access_iterator<int*>>;
+
+ int* begin_ = nullptr;
+ int* end_ = nullptr;
+ constexpr SizedViewWithUnsizedSentinel(int* begin, int* end) : begin_(begin), end_(end) {}
+
+ constexpr auto begin() const { return iterator(begin_); }
+ constexpr auto end() const { return sentinel(iterator(end_)); }
+ constexpr size_t size() const { return end_ - begin_; }
+};
+static_assert(std::ranges::random_access_range<SizedViewWithUnsizedSentinel>);
+static_assert(std::ranges::sized_range<SizedViewWithUnsizedSentinel>);
+static_assert(!std::sized_sentinel_for<SizedViewWithUnsizedSentinel::sentinel, SizedViewWithUnsizedSentinel::iterator>);
+static_assert(std::ranges::view<SizedViewWithUnsizedSentinel>);
+
+template <class T>
+constexpr void test_small_range(const T& input) {
+ constexpr int N = 100;
+ auto size = std::ranges::size(input);
+ assert(size < N);
+
+ auto result = input | std::views::drop(N);
+ assert(result.empty());
+}
+
+constexpr bool test() {
+ constexpr int N = 8;
+ int buf[N] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ // Test that `std::views::drop` is a range adaptor.
+ {
+ using SomeView = SizedView;
+
+ // Test `view | views::drop`
+ {
+ SomeView view(buf, buf + N);
+ std::same_as<std::ranges::drop_view<SomeView>> decltype(auto) result = view | std::views::drop(3);
+ assert(result.base().begin_ == buf);
+ assert(result.base().end_ == buf + N);
+ assert(base(result.begin()) == buf + 3);
+ assert(base(base(result.end())) == buf + N);
+ assert(result.size() == 5);
+ }
+
+ // Test `adaptor | views::drop`
+ {
+ SomeView view(buf, buf + N);
+ auto f = [](int i) { return i; };
+ auto const partial = std::views::transform(f) | std::views::drop(3);
+
+ using Result = std::ranges::drop_view<std::ranges::transform_view<SomeView, decltype(f)>>;
+ std::same_as<Result> decltype(auto) result = partial(view);
+ assert(result.base().base().begin_ == buf);
+ assert(result.base().base().end_ == buf + N);
+ assert(base(result.begin().base()) == buf + 3);
+ assert(base(base(result.end().base())) == buf + N);
+ assert(result.size() == 5);
+ }
+
+ // Test `views::drop | adaptor`
+ {
+ SomeView view(buf, buf + N);
+ auto f = [](int i) { return i; };
+ auto const partial = std::views::drop(3) | std::views::transform(f);
+
+ using Result = std::ranges::transform_view<std::ranges::drop_view<SomeView>, decltype(f)>;
+ std::same_as<Result> decltype(auto) result = partial(view);
+ assert(result.base().base().begin_ == buf);
+ assert(result.base().base().end_ == buf + N);
+ assert(base(result.begin().base()) == buf + 3);
+ assert(base(base(result.end().base())) == buf + N);
+ assert(result.size() == 5);
+ }
+
+ // Check SFINAE friendliness
+ {
+ struct NotAView { };
+ static_assert(!std::is_invocable_v<decltype(std::views::drop)>);
+ static_assert(!std::is_invocable_v<decltype(std::views::drop), NotAView, int>);
+ static_assert( CanBePiped<SomeView&, decltype(std::views::drop(3))>);
+ static_assert( CanBePiped<int(&)[10], decltype(std::views::drop(3))>);
+ static_assert(!CanBePiped<int(&&)[10], decltype(std::views::drop(3))>);
+ static_assert(!CanBePiped<NotAView, decltype(std::views::drop(3))>);
+
+ static_assert(!CanBePiped<SomeView&, decltype(std::views::drop(/*n=*/NotAView{}))>);
+ }
+ }
+
+ {
+ static_assert(std::same_as<decltype(std::views::drop), decltype(std::ranges::views::drop)>);
+ }
+
+ // `views::drop(empty_view, n)` returns an `empty_view`.
+ {
+ using Result = std::ranges::empty_view<int>;
+ [[maybe_unused]] std::same_as<Result> decltype(auto) result = std::views::empty<int> | std::views::drop(3);
+ }
+
+ // `views::drop(span, n)` returns a `span`.
+ {
+ std::span<int> s(buf);
+ std::same_as<decltype(s)> decltype(auto) result = s | std::views::drop(5);
+ assert(result.size() == 3);
+ }
+
+ // `views::drop(span, n)` returns a `span` with a dynamic extent, regardless of the input `span`.
+ {
+ std::span<int, 8> s(buf);
+ std::same_as<std::span<int, std::dynamic_extent>> decltype(auto) result = s | std::views::drop(3);
+ assert(result.size() == 5);
+ }
+
+ // `views::drop(string_view, n)` returns a `string_view`.
+ {
+ {
+ std::string_view sv = "abcdef";
+ std::same_as<decltype(sv)> decltype(auto) result = sv | std::views::drop(2);
+ assert(result.size() == 4);
+ }
+
+ {
+ std::u32string_view sv = U"abcdef";
+ std::same_as<decltype(sv)> decltype(auto) result = sv | std::views::drop(2);
+ assert(result.size() == 4);
+ }
+ }
+
+ // `views::drop(iota_view, n)` returns an `iota_view`.
+ {
+ auto iota = std::views::iota(1, 8);
+ // The second template argument of the resulting `iota_view` is
diff erent because it has to be able to hold
+ // the `range_
diff erence_t` of the input `iota_view`.
+ using Result = std::ranges::iota_view<int, int>;
+ std::same_as<Result> decltype(auto) result = iota | std::views::drop(3);
+ assert(result.size() == 4);
+ assert(*result.begin() == 4);
+ assert(*std::ranges::next(result.begin(), 3) == 7);
+ }
+
+ // `views::drop(subrange, n)` returns a `subrange` when `subrange::StoreSize == false`.
+ {
+ auto subrange = std::ranges::subrange(buf, buf + N);
+ LIBCPP_STATIC_ASSERT(!decltype(subrange)::_StoreSize);
+
+ using Result = std::ranges::subrange<int*>;
+ std::same_as<Result> decltype(auto) result = subrange | std::views::drop(3);
+ assert(result.size() == 5);
+ }
+
+ // `views::drop(subrange, n)` returns a `subrange` when `subrange::StoreSize == true`.
+ {
+ using View = SizedViewWithUnsizedSentinel;
+ View view(buf, buf + N);
+
+ using Subrange = std::ranges::subrange<View::iterator, View::sentinel, std::ranges::subrange_kind::sized>;
+ auto subrange = Subrange(view.begin(), view.end(), std::ranges::distance(view.begin(), view.end()));
+ LIBCPP_STATIC_ASSERT(decltype(subrange)::_StoreSize);
+
+ std::same_as<Subrange> decltype(auto) result = subrange | std::views::drop(3);
+ assert(result.size() == 5);
+ }
+
+ // `views::drop(subrange, n)` doesn't return a `subrange` if it's not a random access range.
+ {
+ SizedView v(buf, buf + N);
+ auto subrange = std::ranges::subrange(v.begin(), v.end());
+
+ using Result = std::ranges::drop_view<std::ranges::subrange<forward_iterator<int*>,
+ sized_sentinel<forward_iterator<int*>>>>;
+ std::same_as<Result> decltype(auto) result = subrange | std::views::drop(3);
+ assert(result.size() == 5);
+ }
+
+ // When the size of the input range `s` is shorter than `n`, an `empty_view` is returned.
+ {
+ test_small_range(std::span(buf));
+ test_small_range(std::string_view("abcdef"));
+ test_small_range(std::ranges::subrange(buf, buf + N));
+ test_small_range(std::views::iota(1, 8));
+ }
+
+ // Test that it's possible to call `std::views::drop` with any single argument as long as the resulting closure is
+ // never invoked. There is no good use case for it, but it's valid.
+ {
+ struct X { };
+ [[maybe_unused]] auto partial = std::views::drop(X{});
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp
index a326f73f77d02..89231ec56607d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp
@@ -13,13 +13,12 @@
#include <ranges>
-#include <array>
#include <cassert>
#include <concepts>
+#include <span>
#include <string_view>
#include <utility>
#include "test_iterators.h"
-#include "types.h"
template <class View, class T>
concept CanBePiped = requires (View&& view, T&& t) {
More information about the libcxx-commits
mailing list