summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur O'Dwyer <arthur.j.odwyer@gmail.com>2021-12-06 15:39:08 -0500
committerArthur O'Dwyer <arthur.j.odwyer@gmail.com>2021-12-07 13:59:41 -0500
commitbd0c0e5b8c8c0bff5ffdff3d1d43dfbacf9caa06 (patch)
tree3113e42d4065dda46433019d462f91295355734f
parent7a06a14f624d31e7ed920f254d8b733d535258cb (diff)
downloadllvm-bd0c0e5b8c8c0bff5ffdff3d1d43dfbacf9caa06.tar.gz
[libc++] [ranges] SFINAE-friendly "write it three times" in views::counted.
Before this patch, the new test's `CountedInvocable<int*, int*>` would hard-error instead of SFINAEing and cleanly returning false. Notice that views::counted specifically does NOT work with pipes; `counted(42)` is ill-formed. This is because `counted`'s first argument is supposed to be an iterator, not a range. Also, mark `views::counted(it, n)` as [[nodiscard]], and test that. (We have a general policy now that range adaptors are consistently marked [[nodiscard]], so that people don't accidentally think that they have side effects. This matters mostly for `reverse` and `transform`, arguably `drop`, and just generally let's be consistent.) Differential Revision: https://reviews.llvm.org/D115177
-rw-r--r--libcxx/include/__ranges/counted.h69
-rw-r--r--libcxx/test/libcxx/ranges/range.adaptors/range.counted/adaptor.nodiscard.verify.cpp21
-rw-r--r--libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp346
-rw-r--r--libcxx/test/support/test_iterators.h2
4 files changed, 241 insertions, 197 deletions
diff --git a/libcxx/include/__ranges/counted.h b/libcxx/include/__ranges/counted.h
index d292bcbb1849..cb9784092420 100644
--- a/libcxx/include/__ranges/counted.h
+++ b/libcxx/include/__ranges/counted.h
@@ -9,6 +9,7 @@
#ifndef _LIBCPP___RANGES_COUNTED_H
#define _LIBCPP___RANGES_COUNTED_H
+#include <__concepts/convertible_to.h>
#include <__config>
#include <__iterator/concepts.h>
#include <__iterator/counted_iterator.h>
@@ -16,10 +17,7 @@
#include <__iterator/incrementable_traits.h>
#include <__iterator/iterator_traits.h>
#include <__memory/pointer_traits.h>
-#include <__ranges/concepts.h>
#include <__ranges/subrange.h>
-#include <__utility/decay_copy.h>
-#include <__utility/declval.h>
#include <__utility/forward.h>
#include <__utility/move.h>
#include <span>
@@ -36,50 +34,39 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace ranges::views {
namespace __counted {
- template<class _From, class _To>
- concept __explicitly_convertible = requires {
- _To(_From{});
- };
struct __fn {
- template<class _Iter, class _Diff>
- requires contiguous_iterator<decay_t<_Iter>> &&
- __explicitly_convertible<_Diff, iter_difference_t<_Iter>>
+ template<contiguous_iterator _It>
_LIBCPP_HIDE_FROM_ABI
- constexpr auto operator()(_Iter&& __it, _Diff __c) const
- noexcept(noexcept(
- span(_VSTD::to_address(__it), static_cast<iter_difference_t<_Iter>>(__c))
- ))
- {
- return span(_VSTD::to_address(__it), static_cast<iter_difference_t<_Iter>>(__c));
- }
-
- template<class _Iter, class _Diff>
- requires random_access_iterator<decay_t<_Iter>> &&
- __explicitly_convertible<_Diff, iter_difference_t<_Iter>>
+ static constexpr auto __go(_It __it, iter_difference_t<_It> __count)
+ noexcept(noexcept(span(_VSTD::to_address(__it), static_cast<size_t>(__count))))
+ // Deliberately omit return-type SFINAE, because to_address is not SFINAE-friendly
+ { return span(_VSTD::to_address(__it), static_cast<size_t>(__count)); }
+
+ template<random_access_iterator _It>
_LIBCPP_HIDE_FROM_ABI
- constexpr auto operator()(_Iter&& __it, _Diff __c) const
- noexcept(
- noexcept(__it + static_cast<iter_difference_t<_Iter>>(__c)) &&
- noexcept(ranges::subrange(_VSTD::forward<_Iter>(__it), _VSTD::__decay_copy(__it)))
- )
- {
- auto __last = __it + static_cast<iter_difference_t<_Iter>>(__c);
- return ranges::subrange(_VSTD::forward<_Iter>(__it), _VSTD::move(__last));
- }
-
- template<class _Iter, class _Diff>
- requires __explicitly_convertible<_Diff, iter_difference_t<_Iter>>
+ static constexpr auto __go(_It __it, iter_difference_t<_It> __count)
+ noexcept(noexcept(subrange(__it, __it + __count)))
+ -> decltype( subrange(__it, __it + __count))
+ { return subrange(__it, __it + __count); }
+
+ template<class _It>
_LIBCPP_HIDE_FROM_ABI
- constexpr auto operator()(_Iter&& __it, _Diff __c) const
- noexcept(noexcept(
- ranges::subrange(counted_iterator(_VSTD::forward<_Iter>(__it), __c), default_sentinel)
- ))
- {
- return ranges::subrange(counted_iterator(_VSTD::forward<_Iter>(__it), __c), default_sentinel);
- }
+ static constexpr auto __go(_It __it, iter_difference_t<_It> __count)
+ noexcept(noexcept(subrange(counted_iterator(_VSTD::move(__it), __count), default_sentinel)))
+ -> decltype( subrange(counted_iterator(_VSTD::move(__it), __count), default_sentinel))
+ { return subrange(counted_iterator(_VSTD::move(__it), __count), default_sentinel); }
+
+ template<class _It, convertible_to<iter_difference_t<_It>> _Diff>
+ requires input_or_output_iterator<decay_t<_It>>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_It&& __it, _Diff&& __count) const
+ noexcept(noexcept(__go(_VSTD::forward<_It>(__it), _VSTD::forward<_Diff>(__count))))
+ -> decltype( __go(_VSTD::forward<_It>(__it), _VSTD::forward<_Diff>(__count)))
+ { return __go(_VSTD::forward<_It>(__it), _VSTD::forward<_Diff>(__count)); }
};
-}
+
+} // namespace __counted
inline namespace __cpo {
inline constexpr auto counted = __counted::__fn{};
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.counted/adaptor.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.counted/adaptor.nodiscard.verify.cpp
new file mode 100644
index 000000000000..dc2c8bafb1b0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.counted/adaptor.nodiscard.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Test the libc++ extension that std::views::counted is marked as [[nodiscard]].
+
+#include <ranges>
+
+void test() {
+ int range[] = {1, 2, 3};
+
+ std::views::counted(range, 1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp
index 2ebd7036db33..29a891174b58 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp
@@ -12,191 +12,225 @@
// std::views::counted;
-#include <concepts>
#include <ranges>
+#include <cassert>
+#include <concepts>
+#include <cstddef>
+#include <memory>
#include <span>
+#include <utility>
-#include <cassert>
#include "test_macros.h"
#include "test_iterators.h"
-struct Unrelated {};
+struct RvalueConvertible {
+ RvalueConvertible(const RvalueConvertible&) = delete;
+ operator int() &&;
+};
-struct ConvertibleToSize {
- constexpr operator std::ptrdiff_t() const { return 8; }
+struct LvalueConvertible {
+ LvalueConvertible(const LvalueConvertible&) = delete;
+ operator int() &;
};
-struct ImplicitlyConvertible {
- operator short();
- explicit operator std::ptrdiff_t() = delete;
+struct OnlyExplicitlyConvertible {
+ explicit operator int() const;
};
-template<class Iter, class T>
-concept CountedInvocable = requires(Iter& i, T t) { std::views::counted(i, t); };
+template<class... Ts>
+concept CountedInvocable = requires (Ts&&... ts) {
+ std::views::counted(std::forward<Ts>(ts)...);
+};
constexpr bool test() {
int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
{
- static_assert( CountedInvocable<contiguous_iterator<int*>, ConvertibleToSize>);
- static_assert(!CountedInvocable<contiguous_iterator<int*>, ImplicitlyConvertible>);
- static_assert(!CountedInvocable<contiguous_iterator<int*>, Unrelated>);
+ static_assert(std::addressof(std::views::counted) == std::addressof(std::ranges::views::counted));
+
+ auto copy = std::views::counted;
+ static_assert(std::semiregular<decltype(copy)>);
+
+ static_assert( CountedInvocable<int*, size_t>);
+ static_assert(!CountedInvocable<int*, LvalueConvertible>);
+ static_assert( CountedInvocable<int*, LvalueConvertible&>);
+ static_assert( CountedInvocable<int*, RvalueConvertible>);
+ static_assert(!CountedInvocable<int*, RvalueConvertible&>);
+ static_assert(!CountedInvocable<int*, OnlyExplicitlyConvertible>);
+ static_assert(!CountedInvocable<int*, int*>);
+ static_assert(!CountedInvocable<int*>);
+ static_assert(!CountedInvocable<size_t>);
+ static_assert(!CountedInvocable<>);
+ }
+
+ {
+ auto c1 = std::views::counted(buffer, 3);
+ auto c2 = std::views::counted(std::as_const(buffer), 3);
+
+ ASSERT_SAME_TYPE(decltype(c1), std::span<int>);
+ ASSERT_SAME_TYPE(decltype(c2), std::span<const int>);
+
+ assert(c1.data() == buffer && c1.size() == 3);
+ assert(c2.data() == buffer && c2.size() == 3);
+ }
+
+ {
+ auto it = contiguous_iterator<int*>(buffer);
+ auto cit = contiguous_iterator<const int*>(buffer);
+
+ auto c1 = std::views::counted(it, 3);
+ auto c2 = std::views::counted(std::as_const(it), 3);
+ auto c3 = std::views::counted(std::move(it), 3);
+ auto c4 = std::views::counted(contiguous_iterator<int*>(buffer), 3);
+ auto c5 = std::views::counted(cit, 3);
+ auto c6 = std::views::counted(std::as_const(cit), 3);
+ auto c7 = std::views::counted(std::move(cit), 3);
+ auto c8 = std::views::counted(contiguous_iterator<const int*>(buffer), 3);
+
+ ASSERT_SAME_TYPE(decltype(c1), std::span<int>);
+ ASSERT_SAME_TYPE(decltype(c2), std::span<int>);
+ ASSERT_SAME_TYPE(decltype(c3), std::span<int>);
+ ASSERT_SAME_TYPE(decltype(c4), std::span<int>);
+ ASSERT_SAME_TYPE(decltype(c5), std::span<const int>);
+ ASSERT_SAME_TYPE(decltype(c6), std::span<const int>);
+ ASSERT_SAME_TYPE(decltype(c7), std::span<const int>);
+ ASSERT_SAME_TYPE(decltype(c8), std::span<const int>);
+
+ assert(c1.data() == buffer && c1.size() == 3);
+ assert(c2.data() == buffer && c2.size() == 3);
+ assert(c3.data() == buffer && c3.size() == 3);
+ assert(c4.data() == buffer && c4.size() == 3);
+ assert(c5.data() == buffer && c5.size() == 3);
+ assert(c6.data() == buffer && c6.size() == 3);
+ assert(c7.data() == buffer && c7.size() == 3);
+ assert(c8.data() == buffer && c8.size() == 3);
+ }
- static_assert(std::semiregular<std::remove_const_t<decltype(std::views::counted)>>);
+ {
+ auto it = random_access_iterator<int*>(buffer);
+ auto cit = random_access_iterator<const int*>(buffer);
+
+ auto c1 = std::views::counted(it, 3);
+ auto c2 = std::views::counted(std::as_const(it), 3);
+ auto c3 = std::views::counted(std::move(it), 3);
+ auto c4 = std::views::counted(random_access_iterator<int*>(buffer), 3);
+ auto c5 = std::views::counted(cit, 3);
+ auto c6 = std::views::counted(std::as_const(cit), 3);
+ auto c7 = std::views::counted(std::move(cit), 3);
+ auto c8 = std::views::counted(random_access_iterator<const int*>(buffer), 3);
+
+ ASSERT_SAME_TYPE(decltype(c1), std::ranges::subrange<random_access_iterator<int*>>);
+ ASSERT_SAME_TYPE(decltype(c2), std::ranges::subrange<random_access_iterator<int*>>);
+ ASSERT_SAME_TYPE(decltype(c3), std::ranges::subrange<random_access_iterator<int*>>);
+ ASSERT_SAME_TYPE(decltype(c4), std::ranges::subrange<random_access_iterator<int*>>);
+ ASSERT_SAME_TYPE(decltype(c5), std::ranges::subrange<random_access_iterator<const int*>>);
+ ASSERT_SAME_TYPE(decltype(c6), std::ranges::subrange<random_access_iterator<const int*>>);
+ ASSERT_SAME_TYPE(decltype(c7), std::ranges::subrange<random_access_iterator<const int*>>);
+ ASSERT_SAME_TYPE(decltype(c8), std::ranges::subrange<random_access_iterator<const int*>>);
+
+ assert(c1.begin() == it && c1.end() == it + 3);
+ assert(c2.begin() == it && c2.end() == it + 3);
+ assert(c3.begin() == it && c3.end() == it + 3);
+ assert(c4.begin() == it && c4.end() == it + 3);
+ assert(c5.begin() == cit && c5.end() == cit + 3);
+ assert(c6.begin() == cit && c6.end() == cit + 3);
+ assert(c7.begin() == cit && c7.end() == cit + 3);
+ assert(c8.begin() == cit && c8.end() == cit + 3);
}
{
- {
- contiguous_iterator<int*> iter(buffer);
- std::span<int> s = std::views::counted(iter, 8);
- assert(s.size() == 8);
- assert(s.data() == buffer);
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::span<int>);
- }
- {
- const contiguous_iterator<int*> iter(buffer);
- std::span<int> s = std::views::counted(iter, 8);
- assert(s.size() == 8);
- assert(s.data() == buffer);
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::span<int>);
- }
- {
- contiguous_iterator<const int*> iter(buffer);
- std::span<const int> s = std::views::counted(iter, 8);
- assert(s.size() == 8);
- assert(s.data() == buffer);
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::span<const int>);
- }
- {
- const contiguous_iterator<const int*> iter(buffer);
- std::span<const int> s = std::views::counted(iter, 8);
- assert(s.size() == 8);
- assert(s.data() == buffer);
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::span<const int>);
- }
+ auto it = bidirectional_iterator<int*>(buffer);
+ auto cit = bidirectional_iterator<const int*>(buffer);
+
+ auto c1 = std::views::counted(it, 3);
+ auto c2 = std::views::counted(std::as_const(it), 3);
+ auto c3 = std::views::counted(std::move(it), 3);
+ auto c4 = std::views::counted(bidirectional_iterator<int*>(buffer), 3);
+ auto c5 = std::views::counted(cit, 3);
+ auto c6 = std::views::counted(std::as_const(cit), 3);
+ auto c7 = std::views::counted(std::move(cit), 3);
+ auto c8 = std::views::counted(bidirectional_iterator<const int*>(buffer), 3);
+
+ using Expected = std::ranges::subrange<std::counted_iterator<decltype(it)>, std::default_sentinel_t>;
+ using ConstExpected = std::ranges::subrange<std::counted_iterator<decltype(cit)>, std::default_sentinel_t>;
+
+ ASSERT_SAME_TYPE(decltype(c1), Expected);
+ ASSERT_SAME_TYPE(decltype(c2), Expected);
+ ASSERT_SAME_TYPE(decltype(c3), Expected);
+ ASSERT_SAME_TYPE(decltype(c4), Expected);
+ ASSERT_SAME_TYPE(decltype(c5), ConstExpected);
+ ASSERT_SAME_TYPE(decltype(c6), ConstExpected);
+ ASSERT_SAME_TYPE(decltype(c7), ConstExpected);
+ ASSERT_SAME_TYPE(decltype(c8), ConstExpected);
+
+ assert(c1.begin().base() == it && c1.size() == 3);
+ assert(c2.begin().base() == it && c2.size() == 3);
+ assert(c3.begin().base() == it && c3.size() == 3);
+ assert(c4.begin().base() == it && c4.size() == 3);
+ assert(c5.begin().base() == cit && c5.size() == 3);
+ assert(c6.begin().base() == cit && c6.size() == 3);
+ assert(c7.begin().base() == cit && c7.size() == 3);
+ assert(c8.begin().base() == cit && c8.size() == 3);
}
{
- {
- random_access_iterator<int*> iter(buffer);
- std::ranges::subrange<random_access_iterator<int*>> s = std::views::counted(iter, 8);
- assert(s.size() == 8);
- assert(s.begin() == iter);
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::ranges::subrange<random_access_iterator<int*>>);
- }
- {
- const random_access_iterator<int*> iter(buffer);
- std::ranges::subrange<random_access_iterator<int*>> s = std::views::counted(iter, 8);
- assert(s.size() == 8);
- assert(s.begin() == iter);
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::ranges::subrange<random_access_iterator<int*>>);
- }
- {
- random_access_iterator<const int*> iter(buffer);
- std::ranges::subrange<random_access_iterator<const int*>> s = std::views::counted(iter, 8);
- assert(s.size() == 8);
- assert(s.begin() == iter);
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::ranges::subrange<random_access_iterator<const int*>>);
- }
- {
- const random_access_iterator<const int*> iter(buffer);
- std::ranges::subrange<random_access_iterator<const int*>> s = std::views::counted(iter, 8);
- assert(s.size() == 8);
- assert(s.begin() == iter);
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::ranges::subrange<random_access_iterator<const int*>>);
- }
+ auto it = output_iterator<int*>(buffer);
+
+ auto c1 = std::views::counted(it, 3);
+ auto c2 = std::views::counted(std::as_const(it), 3);
+ auto c3 = std::views::counted(std::move(it), 3);
+ auto c4 = std::views::counted(output_iterator<int*>(buffer), 3);
+
+ using Expected = std::ranges::subrange<std::counted_iterator<decltype(it)>, std::default_sentinel_t>;
+
+ ASSERT_SAME_TYPE(decltype(c1), Expected);
+ ASSERT_SAME_TYPE(decltype(c2), Expected);
+ ASSERT_SAME_TYPE(decltype(c3), Expected);
+ ASSERT_SAME_TYPE(decltype(c4), Expected);
+
+ assert(base(c1.begin().base()) == buffer && c1.size() == 3);
+ assert(base(c2.begin().base()) == buffer && c2.size() == 3);
+ assert(base(c3.begin().base()) == buffer && c3.size() == 3);
+ assert(base(c4.begin().base()) == buffer && c4.size() == 3);
}
{
- {
- bidirectional_iterator<int*> iter(buffer);
- std::ranges::subrange<
- std::counted_iterator<bidirectional_iterator<int*>>,
- std::default_sentinel_t> s = std::views::counted(iter, 8);
- assert(s.size() == 8);
- assert(s.begin() == std::counted_iterator(iter, 8));
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)),
- std::ranges::subrange<
- std::counted_iterator<bidirectional_iterator<int*>>,
- std::default_sentinel_t>);
- }
- {
- const bidirectional_iterator<int*> iter(buffer);
- std::ranges::subrange<
- std::counted_iterator<bidirectional_iterator<int*>>,
- std::default_sentinel_t> s = std::views::counted(iter, 8);
- assert(s.size() == 8);
- assert(s.begin() == std::counted_iterator(iter, 8));
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)),
- std::ranges::subrange<
- std::counted_iterator<bidirectional_iterator<int*>>,
- std::default_sentinel_t>);
- }
- {
- output_iterator<const int*> iter(buffer);
- std::ranges::subrange<
- std::counted_iterator<output_iterator<const int*>>,
- std::default_sentinel_t> s = std::views::counted(iter, 8);
- assert(s.size() == 8);
- assert(s.begin() == std::counted_iterator(iter, 8));
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)),
- std::ranges::subrange<
- std::counted_iterator<output_iterator<const int*>>,
- std::default_sentinel_t>);
- }
- {
- const output_iterator<const int*> iter(buffer);
- std::ranges::subrange<
- std::counted_iterator<output_iterator<const int*>>,
- std::default_sentinel_t> s = std::views::counted(iter, 8);
- assert(s.size() == 8);
- assert(s.begin() == std::counted_iterator(iter, 8));
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)),
- std::ranges::subrange<
- std::counted_iterator<output_iterator<const int*>>,
- std::default_sentinel_t>);
- }
- {
- cpp20_input_iterator<int*> iter(buffer);
- std::ranges::subrange<
- std::counted_iterator<cpp20_input_iterator<int*>>,
- std::default_sentinel_t> s = std::views::counted(std::move(iter), 8);
- assert(s.size() == 8);
- assert(s.begin().base().base() == buffer);
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(std::move(iter), 8)),
- std::ranges::subrange<
- std::counted_iterator<cpp20_input_iterator<int*>>,
- std::default_sentinel_t>);
- }
- {
- std::ranges::subrange<
- std::counted_iterator<cpp20_input_iterator<int*>>,
- std::default_sentinel_t> s = std::views::counted(cpp20_input_iterator<int*>(buffer), 8);
- assert(s.size() == 8);
- assert(s.begin().base().base() == buffer);
-
- ASSERT_SAME_TYPE(decltype(std::views::counted(cpp20_input_iterator<int*>(buffer), 8)),
- std::ranges::subrange<
- std::counted_iterator<cpp20_input_iterator<int*>>,
- std::default_sentinel_t>);
- }
+ auto it = cpp17_input_iterator<int*>(buffer);
+
+ auto c1 = std::views::counted(it, 3);
+ auto c2 = std::views::counted(std::as_const(it), 3);
+ auto c3 = std::views::counted(std::move(it), 3);
+ auto c4 = std::views::counted(cpp17_input_iterator<int*>(buffer), 3);
+
+ using Expected = std::ranges::subrange<std::counted_iterator<decltype(it)>, std::default_sentinel_t>;
+
+ ASSERT_SAME_TYPE(decltype(c1), Expected);
+ ASSERT_SAME_TYPE(decltype(c2), Expected);
+ ASSERT_SAME_TYPE(decltype(c3), Expected);
+ ASSERT_SAME_TYPE(decltype(c4), Expected);
+
+ assert(base(c1.begin().base()) == buffer && c1.size() == 3);
+ assert(base(c2.begin().base()) == buffer && c2.size() == 3);
+ assert(base(c3.begin().base()) == buffer && c3.size() == 3);
+ assert(base(c4.begin().base()) == buffer && c4.size() == 3);
}
{
- static_assert(std::same_as<decltype(std::views::counted), decltype(std::ranges::views::counted)>);
+ auto it = cpp20_input_iterator<int*>(buffer);
+
+ static_assert(!std::copyable<cpp20_input_iterator<int*>>);
+ static_assert(!CountedInvocable<cpp20_input_iterator<int*>&, int>);
+ static_assert(!CountedInvocable<const cpp20_input_iterator<int*>&, int>);
+ auto c3 = std::views::counted(std::move(it), 3);
+ auto c4 = std::views::counted(cpp20_input_iterator<int*>(buffer), 3);
+
+ using Expected = std::ranges::subrange<std::counted_iterator<decltype(it)>, std::default_sentinel_t>;
+
+ ASSERT_SAME_TYPE(decltype(c3), Expected);
+ ASSERT_SAME_TYPE(decltype(c4), Expected);
+
+ assert(base(c3.begin().base()) == buffer && c3.size() == 3);
+ assert(base(c4.begin().base()) == buffer && c4.size() == 3);
}
return true;
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index d69ceec66066..ef699642cb95 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -657,6 +657,8 @@ struct cpp20_input_iterator {
constexpr I base() && { return std::move(base_); }
+ friend constexpr I base(const cpp20_input_iterator& i) { return i.base_; }
+
template <class T>
void operator,(T const &) = delete;