[libcxx-commits] [libcxx] [libc++][ranges] implement `std::ranges::zip_transform_view` (PR #79605)
via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jan 29 14:37:00 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Hui (huixie90)
<details>
<summary>Changes</summary>
---
Patch is 115.10 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/79605.diff
31 Files Affected:
- (modified) libcxx/docs/Status/RangesViews.csv (+1-1)
- (modified) libcxx/docs/Status/ZipProjects.csv (+1-1)
- (modified) libcxx/include/CMakeLists.txt (+1)
- (added) libcxx/include/__ranges/zip_transform_view.h (+357)
- (modified) libcxx/include/__ranges/zip_view.h (+9)
- (modified) libcxx/include/module.modulemap.in (+1)
- (modified) libcxx/include/ranges (+11)
- (added) libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/no_unique_address.compile.pass.cpp (+38)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/begin.pass.cpp (+72)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/cpo.pass.cpp (+99)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctad.compile.pass.cpp (+36)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.default.pass.cpp (+85)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.views.pass.cpp (+88)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/end.pass.cpp (+99)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/general.pass.cpp (+29)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/arithmetic.pass.cpp (+139)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/compare.pass.cpp (+160)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.default.pass.cpp (+50)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.other.pass.cpp (+63)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/decrement.pass.cpp (+84)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/deref.pass.cpp (+97)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/increment.pass.cpp (+80)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/member_types.compile.pass.cpp (+158)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/subscript.pass.cpp (+68)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/ctor.default.pass.cpp (+51)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/ctor.other.pass.cpp (+76)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/eq.pass.cpp (+142)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/minus.pass.cpp (+209)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/size.pass.cpp (+90)
- (added) libcxx/test/std/ranges/range.adaptors/range.zip.transform/types.h (+500)
- (modified) libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp (+1)
``````````diff
diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv
index f141656eb131a26..1a8d6c3f9402680 100644
--- a/libcxx/docs/Status/RangesViews.csv
+++ b/libcxx/docs/Status/RangesViews.csv
@@ -25,7 +25,7 @@ C++20,`istream <https://wg21.link/P1035R7>`_,Hui Xie,`D133317 <https://llvm.org/
C++23,`repeat <https://wg21.link/P2474R2>`_,Yrong,`D141699 <https://llvm.org/D141699>`_,✅
C++23,`cartesian_product <https://wg21.link/P2374R4>`_,Unassigned,No patch yet,Not started
C++23,`zip <https://wg21.link/P2321R2>`_,Hui Xie,`D122806 <https://llvm.org/D122806>`_,✅
-C++23,`zip_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
+C++23,`zip_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,✅
C++23,`adjacent <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
C++23,`adjacent_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
C++23,`join_with <https://wg21.link/P2441R2>`_,Jakub Mazurkiewicz,`65536 <https://github.com/llvm/llvm-project/pull/65536>`_,In progress
diff --git a/libcxx/docs/Status/ZipProjects.csv b/libcxx/docs/Status/ZipProjects.csv
index 699a382ff66b736..b6d66acd233796c 100644
--- a/libcxx/docs/Status/ZipProjects.csv
+++ b/libcxx/docs/Status/ZipProjects.csv
@@ -12,7 +12,7 @@ Section,Description,Dependencies,Assignee,Complete
| `[range.zip.iterator] <https://wg21.link/range.zip.iterator>`_, "`zip_view::iterator <https://reviews.llvm.org/D122806>`_", None, Hui Xie, |Complete|
| `[range.zip.sentinel] <https://wg21.link/range.zip.sentinel>`_, "`zip_view::sentinel <https://reviews.llvm.org/D122806>`_", None, Hui Xie, |Complete|
| `[range.zip.transform.view] <https://wg21.link/range.zip.transform.view>`_, "zip_transform_view", "| `zip_transform_view::iterator`
-| `zip_transform_view::sentinel`", Hui Xie, |Not Started|
+| `zip_transform_view::sentinel`", Hui Xie, |Complete|
| `[range.zip.transform.iterator] <https://wg21.link/range.zip.transform.iterator>`_, "zip_transform_view::iterator", None, Hui Xie, |Not Started|
| `[range.zip.transform.sentinel] <https://wg21.link/range.zip.transform.sentinel>`_, "zip_transform_view::sentinel", None, Hui Xie, |Not Started|
| `[range.adjacent.view] <https://wg21.link/range.adjacent.view>`_, "adjacent_view", "| `adjacent_view::iterator`
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index ed721d467e94f4c..15d4de2fbe6bccd 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -653,6 +653,7 @@ set(files
__ranges/view_interface.h
__ranges/views.h
__ranges/zip_view.h
+ __ranges/zip_transform_view.h
__split_buffer
__std_clang_module
__std_mbstate_t.h
diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
new file mode 100644
index 000000000000000..4cc6ef55e8181ef
--- /dev/null
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -0,0 +1,357 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___RANGES_ZIP_TRANSFORM_VIEW_H
+#define _LIBCPP___RANGES_ZIP_TRANSFORM_VIEW_H
+
+#include <__config>
+
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/derived_from.h>
+#include <__concepts/equality_comparable.h>
+#include <__concepts/invocable.h>
+#include <__functional/invoke.h>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/empty_view.h>
+#include <__ranges/movable_box.h>
+#include <__ranges/view_interface.h>
+#include <__ranges/zip_view.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/maybe_const.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <tuple>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <move_constructible _Fn, input_range... _Views>
+ requires(view<_Views> && ...) &&
+ (sizeof...(_Views) > 0) && is_object_v<_Fn> && regular_invocable<_Fn&, range_reference_t<_Views>...> &&
+ __can_reference<invoke_result_t<_Fn&, range_reference_t<_Views>...>>
+class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views...>> {
+ _LIBCPP_NO_UNIQUE_ADDRESS zip_view<_Views...> __zip_;
+ _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Fn> __fun_;
+
+ using _InnerView = zip_view<_Views...>;
+ template <bool _Const>
+ using __ziperator = iterator_t<__maybe_const<_Const, _InnerView>>;
+ template <bool Const>
+ using __zentinel = sentinel_t<__maybe_const<Const, _InnerView>>;
+
+ template <bool>
+ class __iterator;
+
+ template <bool>
+ class __sentinel;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI zip_transform_view() = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views)
+ : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+ requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
+ {
+ return __iterator<true>(*this, __zip_.begin());
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+ if constexpr (common_range<_InnerView>) {
+ return __iterator<false>(*this, __zip_.end());
+ } else {
+ return __sentinel<false>(__zip_.end());
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+ requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
+ {
+ if constexpr (common_range<const _InnerView>) {
+ return __iterator<true>(*this, __zip_.end());
+ } else {
+ return __sentinel<true>(__zip_.end());
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+ requires sized_range<_InnerView>
+ {
+ return __zip_.size();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+ requires sized_range<const _InnerView>
+ {
+ return __zip_.size();
+ }
+};
+
+template <class _Fn, class... _Ranges>
+zip_transform_view(_Fn, _Ranges&&...) -> zip_transform_view<_Fn, views::all_t<_Ranges>...>;
+
+template <bool _Const, class... _Views>
+concept __base_forward = forward_range<__maybe_const<_Const, zip_view<_Views...>>>;
+
+template <bool _Const, class _Fn, class... _Views>
+struct __zip_transform_iterator_category_base {};
+
+template <bool _Const, class _Fn, class... _Views>
+ requires __base_forward<_Const, _Views...>
+struct __zip_transform_iterator_category_base<_Const, _Fn, _Views...> {
+private:
+ template <class _View>
+ using __tag = typename iterator_traits<iterator_t<__maybe_const<_Const, _View>>>::iterator_category;
+
+ static consteval auto __get_iterator_category() {
+ if constexpr (!is_reference_v<invoke_result_t<__maybe_const<_Const, _Fn>&,
+ range_reference_t<__maybe_const<_Const, _Views>>...>>) {
+ return input_iterator_tag();
+ } else if constexpr ((derived_from<__tag<_Views>, random_access_iterator_tag> && ...)) {
+ return random_access_iterator_tag();
+ } else if constexpr ((derived_from<__tag<_Views>, bidirectional_iterator_tag> && ...)) {
+ return bidirectional_iterator_tag();
+ } else if constexpr ((derived_from<__tag<_Views>, forward_iterator_tag> && ...)) {
+ return forward_iterator_tag();
+ } else {
+ return input_iterator_tag();
+ }
+ }
+
+public:
+ using iterator_category = decltype(__get_iterator_category());
+};
+
+template <move_constructible _Fn, input_range... _Views>
+ requires(view<_Views> && ...) &&
+ (sizeof...(_Views) > 0) && is_object_v<_Fn> && regular_invocable<_Fn&, range_reference_t<_Views>...> &&
+ __can_reference<invoke_result_t<_Fn&, range_reference_t<_Views>...>>
+template <bool _Const>
+class zip_transform_view<_Fn, _Views...>::__iterator
+ : public __zip_transform_iterator_category_base<_Const, _Fn, _Views...> {
+ using _Parent = __maybe_const<_Const, zip_transform_view>;
+ using _Base = __maybe_const<_Const, _InnerView>;
+
+ friend zip_transform_view<_Fn, _Views...>;
+
+ _Parent* __parent_ = nullptr;
+ __ziperator<_Const> __inner_;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, __ziperator<_Const> __inner)
+ : __parent_(std::addressof(__parent)), __inner_(std::move(__inner)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto __get_deref_and_invoke() const noexcept {
+ return [this](const auto&... __iters) noexcept(
+ noexcept(std::invoke(*__parent_->__fun_, *__iters...))) -> decltype(auto) {
+ return std::invoke(*__parent_->__fun_, *__iters...);
+ };
+ }
+
+public:
+ using iterator_concept = typename __ziperator<_Const>::iterator_concept;
+ using value_type =
+ remove_cvref_t<invoke_result_t<__maybe_const<_Const, _Fn>&, range_reference_t<__maybe_const<_Const, _Views>>...>>;
+ using difference_type = range_difference_t<_Base>;
+
+ _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+ requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>>
+ : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+ noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) {
+ return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+ ++__inner_;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int)
+ requires forward_range<_Base>
+ {
+ auto __tmp = *this;
+ ++*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+ requires bidirectional_range<_Base>
+ {
+ --__inner_;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+ requires bidirectional_range<_Base>
+ {
+ auto __tmp = *this;
+ --*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x)
+ requires random_access_range<_Base>
+ {
+ __inner_ += __x;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x)
+ requires random_access_range<_Base>
+ {
+ __inner_ -= __x;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+ requires random_access_range<_Base>
+ {
+ return std::apply(
+ [&]<class... _Is>(const _Is&... __iters) -> decltype(auto) {
+ return std::invoke(*__parent_->__fun_, __iters[iter_difference_t<_Is>(__n)]...);
+ },
+ __zip_view_iterator_access::__get_underlying(__inner_));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y)
+ requires equality_comparable<__ziperator<_Const>>
+ {
+ return __x.__inner_ == __y.__inner_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+ requires random_access_range<_Base>
+ {
+ return __x.__inner_ <=> __y.__inner_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+ requires random_access_range<_Base>
+ {
+ return __iterator(*__i.__parent_, __i.__inner_ + __n);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+ requires random_access_range<_Base>
+ {
+ return __iterator(*__i.__parent_, __i.__inner_ + __n);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+ requires random_access_range<_Base>
+ {
+ return __iterator(*__i.__parent_, __i.__inner_ - __n);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+ requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
+ {
+ return __x.__inner_ - __y.__inner_;
+ }
+};
+
+template <move_constructible _Fn, input_range... _Views>
+ requires(view<_Views> && ...) &&
+ (sizeof...(_Views) > 0) && is_object_v<_Fn> && regular_invocable<_Fn&, range_reference_t<_Views>...> &&
+ __can_reference<invoke_result_t<_Fn&, range_reference_t<_Views>...>>
+template <bool _Const>
+class zip_transform_view<_Fn, _Views...>::__sentinel {
+ __zentinel<_Const> __inner_;
+
+ friend zip_transform_view<_Fn, _Views...>;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(__zentinel<_Const> __inner) : __inner_(__inner) {}
+
+public:
+ _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __i)
+ requires _Const && convertible_to<__zentinel<false>, __zentinel<_Const>>
+ : __inner_(__i.__inner_) {}
+
+ template <bool _OtherConst>
+ requires sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+ return __x.__inner_ == __y.__inner_;
+ }
+
+ template <bool _OtherConst>
+ requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
+ _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+ operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+ return __x.__inner_ - __y.__inner_;
+ }
+
+ template <bool _OtherConst>
+ requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
+ _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+ operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
+ return __x.__inner_ - __y.__inner_;
+ }
+};
+
+namespace views {
+namespace __zip_transform {
+
+struct __fn {
+ template <class _Fn>
+ requires(move_constructible<decay_t<_Fn>> && regular_invocable<decay_t<_Fn>&> &&
+ is_object_v<invoke_result_t<decay_t<_Fn>&>>)
+ _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
+ noexcept(noexcept(auto(views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>))) {
+ return views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>;
+ }
+
+ template <class _Fn, class... _Ranges>
+ requires(sizeof...(_Ranges) > 0)
+ _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
+ noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)))
+ -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) {
+ return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...);
+ }
+};
+
+} // namespace __zip_transform
+inline namespace __cpo {
+inline constexpr auto zip_transform = __zip_transform::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_ZIP_TRANSFORM_VIEW_H
diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h
index 4898c0afc87a6e7..deb87d359c60256 100644
--- a/libcxx/include/__ranges/zip_view.h
+++ b/libcxx/include/__ranges/zip_view.h
@@ -245,6 +245,13 @@ struct __zip_view_iterator_category_base<_Const, _Views...> {
using iterator_category = input_iterator_tag;
};
+struct __zip_view_iterator_access {
+ template <class _Iter>
+ _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_underlying(_Iter& __iter) noexcept {
+ return (__iter.__current_);
+ }
+};
+
template <input_range... _Views>
requires(view<_Views> && ...) && (sizeof...(_Views) > 0)
template <bool _Const>
@@ -263,6 +270,8 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
friend class zip_view<_Views...>;
+ friend __zip_view_iterator_access;
+
public:
using iterator_concept = decltype(__get_zip_view_iterator_tag<_Const, _Views...>());
using value_type = __tuple_or_pair<range_value_t<__maybe_const<_Const, _Views>>...>;
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 194a74a1e07b145..0a3ba7ea49d3b10 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1719,6 +1719,7 @@ module std_private_ranges_transform_view [system] {
module std_private_ranges_view_interface [system] { header "__ranges/view_interface.h" }
module std_private_ranges_views [system] { header "__ranges/views.h" }
module std_private_ranges_zip_view [system] { header "__ranges/zip_view.h" }
+module std_private_ranges_zip_transform_view [system] { header "__ranges/zip_transform_view.h" }
module std_private_span_span_fwd [system] { header "__fwd/span.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 660d533b2a7830a..2a692567261e4cb 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -325,6 +325,16 @@ namespace std::ranges {
namespace views { inline constexpr unspecified zip = unspecified; } // C++23
+ // [range.zip.transform], zip transform view
+ template<move_constructible F, input_range... Views>
+ requires (view<Views> && ...) && (sizeof...(Views) > 0) && is_object_v<F> &&
+ regular_invocable<F&, range_reference_t<Views>...> &&
+ can-reference<invoke_result_t<F&, range_reference_t<Views>...>>
+ class zip_transform_view; // C++23
+
+ namespace views { inline constexpr unspecified zip_transform = unspecified; } // C++23
+
+
// [range.as.rvalue]
template <view V>
requires input_range<V>
@@ -413,6 +423,7 @@ namespace std {
#include <__ranges/transform_view.h>
#include <__ranges/view_interface.h>
#include <__ranges/views.h>
+#include <__ranges/zip_transform_view.h>
#include <__ranges/zip_view.h>
#include <version>
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/no_unique_address.compile.pass.cpp
new file mode 100644
index 000000000000000..7d15a819a381892
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/no_unique_address.compile.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+// XFAIL: msvc
+
+// This test ensures that we use `[[no_unique_address]]` in `zip_transform_view`.
+
+#include <ranges>
+
+struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+};
+
+struct Pred {
+ template <class... Args>
+ bool operator()(const Args&...) const;
+};
+
+template <class View>
+struct Test {
+ [[no_unique_address]] View view;
+ char c;
+};
+
+// [[no_unique_address]] applied to movable-box
+struct PredWithPadding : Pred {
+ alignas(128) char c;
+};
+
+static_assert(sizeof(Test<std::ranges::zip_transform_view<PredWithPadding, View>>) ==
+ sizeof(std::ranges::zip_transform_view<PredWithPad...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/79605
More information about the libcxx-commits
mailing list