[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