forked from OSchip/llvm-project
[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
This commit is contained in:
parent
7a06a14f62
commit
bd0c0e5b8c
|
@ -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));
|
||||
}
|
||||
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<class _Iter, class _Diff>
|
||||
requires random_access_iterator<decay_t<_Iter>> &&
|
||||
__explicitly_convertible<_Diff, iter_difference_t<_Iter>>
|
||||
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));
|
||||
}
|
||||
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 _Iter, class _Diff>
|
||||
requires __explicitly_convertible<_Diff, iter_difference_t<_Iter>>
|
||||
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{};
|
||||
|
|
|
@ -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}}
|
||||
}
|
|
@ -12,191 +12,225 @@
|
|||
|
||||
// std::views::counted;
|
||||
|
||||
#include <concepts>
|
||||
#include <ranges>
|
||||
#include <span>
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "test_iterators.h"
|
||||
|
||||
struct Unrelated {};
|
||||
|
||||
struct ConvertibleToSize {
|
||||
constexpr operator std::ptrdiff_t() const { return 8; }
|
||||
struct RvalueConvertible {
|
||||
RvalueConvertible(const RvalueConvertible&) = delete;
|
||||
operator int() &&;
|
||||
};
|
||||
|
||||
struct ImplicitlyConvertible {
|
||||
operator short();
|
||||
explicit operator std::ptrdiff_t() = delete;
|
||||
struct LvalueConvertible {
|
||||
LvalueConvertible(const LvalueConvertible&) = delete;
|
||||
operator int() &;
|
||||
};
|
||||
|
||||
template<class Iter, class T>
|
||||
concept CountedInvocable = requires(Iter& i, T t) { std::views::counted(i, t); };
|
||||
struct OnlyExplicitlyConvertible {
|
||||
explicit operator int() const;
|
||||
};
|
||||
|
||||
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));
|
||||
|
||||
static_assert(std::semiregular<std::remove_const_t<decltype(std::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<>);
|
||||
}
|
||||
|
||||
{
|
||||
{
|
||||
contiguous_iterator<int*> iter(buffer);
|
||||
std::span<int> s = std::views::counted(iter, 8);
|
||||
assert(s.size() == 8);
|
||||
assert(s.data() == buffer);
|
||||
auto c1 = std::views::counted(buffer, 3);
|
||||
auto c2 = std::views::counted(std::as_const(buffer), 3);
|
||||
|
||||
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(c1), std::span<int>);
|
||||
ASSERT_SAME_TYPE(decltype(c2), std::span<const int>);
|
||||
|
||||
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>);
|
||||
}
|
||||
assert(c1.data() == buffer && c1.size() == 3);
|
||||
assert(c2.data() == buffer && c2.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);
|
||||
auto it = contiguous_iterator<int*>(buffer);
|
||||
auto cit = contiguous_iterator<const int*>(buffer);
|
||||
|
||||
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);
|
||||
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(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(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_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*>>);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
{
|
||||
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));
|
||||
auto it = random_access_iterator<int*>(buffer);
|
||||
auto cit = random_access_iterator<const int*>(buffer);
|
||||
|
||||
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));
|
||||
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(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(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_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>);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
static_assert(std::same_as<decltype(std::views::counted), decltype(std::ranges::views::counted)>);
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue