183 lines
5.6 KiB
C++
183 lines
5.6 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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
|
|
|
|
// constexpr auto begin();
|
|
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <ranges>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include "test_iterators.h"
|
|
|
|
struct View : std::ranges::view_base {
|
|
int* begin() const;
|
|
int* end() const;
|
|
};
|
|
|
|
// Test that begin is not const
|
|
template <class T>
|
|
concept HasBegin = requires(T t) { t.begin(); };
|
|
|
|
struct Pred {
|
|
constexpr bool operator()(int i) const { return i < 3; }
|
|
};
|
|
|
|
static_assert(HasBegin<std::ranges::drop_while_view<View, Pred>>);
|
|
static_assert(!HasBegin<const std::ranges::drop_while_view<View, Pred>>);
|
|
|
|
constexpr auto always = [](auto v) { return [v](auto&&...) { return v; }; };
|
|
|
|
template <class Iter>
|
|
constexpr void testOne() {
|
|
using Sent = sentinel_wrapper<Iter>;
|
|
using Range = std::ranges::subrange<Iter, Sent>;
|
|
constexpr auto make_subrange = []<std::size_t N>(int(&buffer)[N]) {
|
|
return Range{Iter{buffer}, Sent{Iter{buffer + N}}};
|
|
};
|
|
|
|
// empty
|
|
{
|
|
std::array<int, 0> a;
|
|
Range range{Iter{a.data()}, Sent{Iter{a.data() + a.size()}}};
|
|
std::ranges::drop_while_view dwv{std::move(range), always(false)};
|
|
std::same_as<Iter> decltype(auto) it = dwv.begin();
|
|
assert(base(it) == a.data() + a.size());
|
|
}
|
|
|
|
// 1 element not dropped
|
|
{
|
|
int buffer[] = {1};
|
|
auto range = make_subrange(buffer);
|
|
std::ranges::drop_while_view dwv{std::move(range), always(false)};
|
|
std::same_as<Iter> decltype(auto) it = dwv.begin();
|
|
assert(base(it) == buffer);
|
|
}
|
|
|
|
// 1 element dropped
|
|
{
|
|
int buffer[] = {1};
|
|
auto range = make_subrange(buffer);
|
|
std::ranges::drop_while_view dwv{std::move(range), always(true)};
|
|
std::same_as<Iter> decltype(auto) it = dwv.begin();
|
|
assert(base(it) == buffer + 1);
|
|
}
|
|
|
|
// multiple elements. no element dropped
|
|
{
|
|
int buffer[] = {1, 2, 3, 4, 5};
|
|
auto range = make_subrange(buffer);
|
|
std::ranges::drop_while_view dwv{std::move(range), always(false)};
|
|
std::same_as<Iter> decltype(auto) it = dwv.begin();
|
|
assert(base(it) == buffer);
|
|
}
|
|
|
|
// multiple elements. all elements dropped
|
|
{
|
|
int buffer[] = {1, 2, 3, 4, 5};
|
|
auto range = make_subrange(buffer);
|
|
std::ranges::drop_while_view dwv{std::move(range), always(true)};
|
|
std::same_as<Iter> decltype(auto) it = dwv.begin();
|
|
assert(base(it) == buffer + 5);
|
|
}
|
|
|
|
// multiple elements. some elements dropped
|
|
{
|
|
int buffer[] = {1, 2, 3, 2, 1};
|
|
auto range = make_subrange(buffer);
|
|
std::ranges::drop_while_view dwv{std::move(range), [](int i) { return i < 3; }};
|
|
std::same_as<Iter> decltype(auto) it = dwv.begin();
|
|
assert(base(it) == buffer + 2);
|
|
}
|
|
|
|
// Make sure we do not make a copy of the predicate when we call begin()
|
|
{
|
|
struct TrackingPred {
|
|
constexpr explicit TrackingPred(bool* moved, bool* copied) : moved_(moved), copied_(copied) {}
|
|
constexpr TrackingPred(TrackingPred const& other) : moved_(other.moved_), copied_(other.copied_) {
|
|
*copied_ = true;
|
|
}
|
|
constexpr TrackingPred(TrackingPred&& other) : moved_(other.moved_), copied_(other.copied_) { *moved_ = true; }
|
|
TrackingPred& operator=(TrackingPred const&) = default;
|
|
TrackingPred& operator=(TrackingPred&&) = default;
|
|
|
|
constexpr bool operator()(int i) const { return i < 3; }
|
|
bool* moved_;
|
|
bool* copied_;
|
|
};
|
|
|
|
int buffer[] = {1, 2, 3, 2, 1};
|
|
bool moved = false, copied = false;
|
|
auto range = make_subrange(buffer);
|
|
std::ranges::drop_while_view dwv{std::move(range), TrackingPred(&moved, &copied)};
|
|
moved = false;
|
|
copied = false;
|
|
[[maybe_unused]] auto it = dwv.begin();
|
|
assert(!moved);
|
|
assert(!copied);
|
|
}
|
|
|
|
// Test with a non-const predicate
|
|
{
|
|
int buffer[] = {1, 2, 3, 2, 1};
|
|
auto range = make_subrange(buffer);
|
|
std::ranges::drop_while_view dwv{std::move(range), [](int i) mutable { return i < 3; }};
|
|
std::same_as<Iter> decltype(auto) it = dwv.begin();
|
|
assert(base(it) == buffer + 2);
|
|
}
|
|
|
|
// Test with a predicate that takes by non-const reference
|
|
{
|
|
int buffer[] = {1, 2, 3, 2, 1};
|
|
auto range = make_subrange(buffer);
|
|
std::ranges::drop_while_view dwv{std::move(range), [](int& i) { return i < 3; }};
|
|
std::same_as<Iter> decltype(auto) it = dwv.begin();
|
|
assert(base(it) == buffer + 2);
|
|
}
|
|
|
|
if constexpr (std::forward_iterator<Iter>) {
|
|
// Make sure that we cache the result of begin() on subsequent calls
|
|
{
|
|
int buffer[] = {1, 2, 3, 2, 1};
|
|
auto range = make_subrange(buffer);
|
|
|
|
int called = 0;
|
|
auto pred = [&](int i) {
|
|
++called;
|
|
return i < 3;
|
|
};
|
|
std::ranges::drop_while_view dwv{range, pred};
|
|
for (auto i = 0; i < 10; ++i) {
|
|
std::same_as<Iter> decltype(auto) it = dwv.begin();
|
|
assert(base(it) == buffer + 2);
|
|
assert(called == 3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
constexpr bool test() {
|
|
testOne<cpp17_input_iterator<int*>>();
|
|
testOne<cpp20_input_iterator<int*>>();
|
|
testOne<forward_iterator<int*>>();
|
|
testOne<bidirectional_iterator<int*>>();
|
|
testOne<random_access_iterator<int*>>();
|
|
testOne<contiguous_iterator<int*>>();
|
|
testOne<int*>();
|
|
return true;
|
|
}
|
|
|
|
int main(int, char**) {
|
|
test();
|
|
static_assert(test());
|
|
return 0;
|
|
}
|