Jakub Mazurkiewicz 1bb2328fd3
[libc++] Implement views::join_with (#65536)
* Implement "P2441R2 `views::join_with`" (https://wg21.link/P2441R2),
closes #105185
* Implement LWG4074 (https://wg21.link/LWG4074), closes #105346
* Complete implementation of "P2711R1 Making multi-param constructors of
views explicit" (https://wg21.link/P2711R1), closes #105252
* Complete implementation of "P2770R0 Stashing stashing iterators for
proper flattening" (https://wg21.link/P2770R0), closes #105250
2025-06-21 10:54:50 +01:00

133 lines
5.2 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
//
//===----------------------------------------------------------------------===//
// REQUIRES: std-at-least-c++23
// <ranges>
// constexpr View base() const& requires copy_constructible<View>;
// constexpr View base() &&;
#include <ranges>
#include <cassert>
#include <utility>
#include <vector>
using InnerRange = std::vector<int>;
struct Range : std::ranges::view_base {
constexpr explicit Range(InnerRange* b, InnerRange* e) : begin_(b), end_(e) {}
constexpr Range(const Range& other) : begin_(other.begin_), end_(other.end_), was_copy_initialized_(true) {}
constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), was_move_initialized_(true) {}
Range& operator=(const Range&) = default;
Range& operator=(Range&&) = default;
constexpr InnerRange* begin() const { return begin_; }
constexpr InnerRange* end() const { return end_; }
InnerRange* begin_;
InnerRange* end_;
bool was_copy_initialized_ = false;
bool was_move_initialized_ = false;
};
static_assert(std::ranges::view<Range>);
static_assert(std::ranges::input_range<Range>);
struct Pattern : std::ranges::view_base {
static constexpr int pat[2] = {0, 0};
constexpr const int* begin() const { return pat; }
constexpr const int* end() const { return pat + 2; }
};
static_assert(std::ranges::view<Pattern>);
static_assert(std::ranges::forward_range<Pattern>);
template <class Tp>
struct NonCopyableRange : std::ranges::view_base {
NonCopyableRange(const NonCopyableRange&) = delete;
NonCopyableRange(NonCopyableRange&&) = default;
NonCopyableRange& operator=(const NonCopyableRange&) = default;
NonCopyableRange& operator=(NonCopyableRange&&) = default;
Tp* begin() const;
Tp* end() const;
};
static_assert(!std::copy_constructible<NonCopyableRange<InnerRange>>);
static_assert(!std::copy_constructible<NonCopyableRange<int>>);
template <typename T>
concept CanCallBaseOn = requires(T&& t) { std::forward<T>(t).base(); };
constexpr bool test() {
InnerRange buff[3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
Pattern pattern;
{ // Check the const& overload
Range range(buff, buff + 3);
std::ranges::join_with_view<Range, Pattern> view(range, pattern);
std::same_as<Range> decltype(auto) result = view.base();
assert(result.was_copy_initialized_);
assert(result.begin() == buff);
assert(result.end() == buff + 3);
}
{ // Check the const& overload on const `view`
Range range(buff, buff + 3);
const std::ranges::join_with_view<Range, Pattern> view(range, pattern);
std::same_as<Range> decltype(auto) result = view.base();
assert(result.was_copy_initialized_);
assert(result.begin() == buff);
assert(result.end() == buff + 3);
}
{ // Check the && overload
Range range(buff, buff + 3);
std::ranges::join_with_view<Range, Pattern> view(range, pattern);
std::same_as<Range> decltype(auto) result = std::move(view).base();
assert(result.was_move_initialized_);
assert(result.begin() == buff);
assert(result.end() == buff + 3);
}
{ // Ensure the const& overload is not considered when the base is not copy-constructible
static_assert(!CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&>);
static_assert(!CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&>);
static_assert(!CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&&>);
static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&&>);
static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>>);
}
{ // Ensure the const& overload does not depend on Pattern's copy-constructability
static_assert(CanCallBaseOn<const std::ranges::join_with_view<Range, NonCopyableRange<int>>&>);
static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>&>);
static_assert(CanCallBaseOn<const std::ranges::join_with_view<Range, NonCopyableRange<int>>&&>);
static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>&&>);
static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>>);
}
{ // Check above two at the same time
static_assert(
!CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&>);
static_assert(!CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&>);
static_assert(
!CanCallBaseOn<const std::ranges::join_with_view< NonCopyableRange<InnerRange>, NonCopyableRange<int>>&&>);
static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&&>);
static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}