libstdc++: Make non-propagating-cache fully constexpr [PR101263]
authorJonathan Wakely <jwakely@redhat.com>
Fri, 15 Oct 2021 13:49:21 +0000 (14:49 +0100)
committerJonathan Wakely <jwakely@redhat.com>
Fri, 15 Oct 2021 17:26:53 +0000 (18:26 +0100)
libstdc++-v3/ChangeLog:

PR libstdc++/101263
* include/std/ranges (__cached): New wrapper struct.
(__non_propagating_cache): Use __cached for contained value.
(__non_propagating_cache::_M_emplace_deref): Add constexpr. Use
std::construct_at instead of placement new.
* testsuite/std/ranges/adaptors/join.cc: Check constexpr works.

libstdc++-v3/include/std/ranges
libstdc++-v3/testsuite/std/ranges/adaptors/join.cc

index 07eae0c..b8de400 100644 (file)
@@ -1160,8 +1160,33 @@ namespace views::__adaptor
       };
 
     template<typename _Tp>
+      struct __cached
+      {
+       struct _Deref_t { };
+       static constexpr _Deref_t __deref{};
+
+       // Initialize _M_t directly from the result of dereferencing __i.
+       // This avoids any unwanted temporary materialization that would
+       // occur if *__i was bound to a reference before initializing _M_t.
+       template<typename _Iter>
+         constexpr explicit
+         __cached(_Deref_t, _Iter&& __i)
+         : _M_t(*__i)
+         { }
+
+       template<typename... _Args>
+         constexpr explicit
+         __cached(_Args&&... __args)
+         : _M_t(std::forward<_Args>(__args)...)
+         { }
+
+       _Tp _M_t;
+      };
+
+    template<typename _Tp>
       requires is_object_v<_Tp>
-      struct __non_propagating_cache<_Tp> : protected _Optional_base<_Tp>
+      struct __non_propagating_cache<_Tp>
+      : protected _Optional_base<__cached<_Tp>>
       {
        __non_propagating_cache() = default;
 
@@ -1205,23 +1230,22 @@ namespace views::__adaptor
 
        constexpr _Tp&
        operator*() noexcept
-       { return this->_M_get(); }
+       { return this->_M_get()._M_t; }
 
        constexpr const _Tp&
        operator*() const noexcept
-       { return this->_M_get(); }
+       { return this->_M_get()._M_t; }
 
        template<typename _Iter>
-         _Tp&
+         constexpr _Tp&
          _M_emplace_deref(const _Iter& __i)
          {
            this->_M_reset();
-           // Using _Optional_base::_M_construct to initialize from '*__i'
-           // would incur an extra move due to the indirection, so we instead
-           // use placement new directly.
-           ::new ((void *) std::__addressof(this->_M_payload._M_payload)) _Tp(*__i);
+           // Use the special constructor of __cached<_Tp> that does *__i.
+           std::construct_at(std::__addressof(this->_M_payload._M_payload),
+                             std::in_place, __cached<_Tp>::__deref, __i);
            this->_M_payload._M_engaged = true;
-           return this->_M_get();
+           return **this;
          }
       };
 
index 50af3fd..1ec4238 100644 (file)
@@ -193,6 +193,18 @@ test11()
     ;
 }
 
+void
+test12()
+{
+  // PR libstdc++/101263
+  constexpr auto b = [] {
+    auto r = std::views::iota(0, 5)
+      | std::views::lazy_split(0)
+      | std::views::join;
+    return r.begin() != r.end();
+  }();
+}
+
 int
 main()
 {
@@ -207,4 +219,5 @@ main()
   test09();
   test10();
   test11();
+  test12();
 }