std::uninitialized_move{,_n} delegates to the corresponding
std::uninitialized_copy() variant after wrapping with a move
iterator, but the std::uninitialized_copy() doesn't unwrap the
move iterator, therefore losing the memcpy optimization if the
iterators were just pointers.

Fix this by unwrapping the move iterator using  __miter_base(). To
prevent some greedy_ops tests starting to fail by consuming the
pointer difference, we have to unwrap the move iterator before computing
the iterator difference as well.

libstdc++v3/Changelog:

        PR libstdc++/121789
        * include/bits/stl_uninitialized.h (uninitialized_copy):
        Unwrap move iterators
        * testsuite/20_util/specialized_algorithms/uninitialized_move/121789.cc:
        New test.
---

Disclosure: I was assisted by AI.

 libstdc++-v3/include/bits/stl_uninitialized.h |  8 ++--
 .../uninitialized_move/121789.cc              | 37 +++++++++++++++++++
 2 files changed, 42 insertions(+), 3 deletions(-)
 create mode 100644 
libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/121789.cc

diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h 
b/libstdc++-v3/include/bits/stl_uninitialized.h
index 82e4ba2ff7b..fa9d896fefc 100644
--- a/libstdc++-v3/include/bits/stl_uninitialized.h
+++ b/libstdc++-v3/include/bits/stl_uninitialized.h
@@ -273,11 +273,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // We cannot tell when this condition is true in general,
       // so we rely on the __memcpyable trait.
 
 #if __cplusplus >= 201103L
       using _Dest = decltype(std::__niter_base(__result));
-      using _Src = decltype(std::__niter_base(__first));
+      using _Src = decltype(std::__miter_base(std::__niter_base(__first)));
       using _ValT = typename iterator_traits<_ForwardIterator>::value_type;
 
 #if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26
       if consteval {
        return std::__do_uninit_copy(__first, __last, __result);
@@ -285,16 +285,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
       if constexpr (!__is_trivially_constructible(_ValT, decltype(*__first)))
        return std::__do_uninit_copy(__first, __last, __result);
       else if constexpr (__memcpyable<_Dest, _Src>::__value)
        {
-         ptrdiff_t __n = __last - __first;
+         ptrdiff_t __n
+           = std::__miter_base(std::__niter_base(__last))
+             - std::__miter_base(std::__niter_base(__first));
          if (__n > 0) [[__likely__]]
            {
              using _ValT = typename remove_pointer<_Src>::type;
              __builtin_memcpy(std::__niter_base(__result),
-                              std::__niter_base(__first),
+                              std::__miter_base(std::__niter_base(__first)),
                               __n * sizeof(_ValT));
              __result += __n;
            }
          return __result;
        }
diff --git 
a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/121789.cc
 
b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/121789.cc
new file mode 100644
index 00000000000..7ac8cf6496f
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/121789.cc
@@ -0,0 +1,37 @@
+// Copyright (C) 2025-2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-O1 -fdump-tree-optimized" }
+// { dg-do compile { target c++17 } }
+// { dg-final { scan-tree-dump "memcpy" "optimized" } }
+
+// PR libstdc++/121789
+// std::uninitialized_move_n() and friends don't optimize to memcpy
+
+#include <memory>
+
+struct T { int x; };
+
+void f(T* src, T* dst, unsigned n)
+{
+  std::uninitialized_move(src, src + n, dst);
+}
+
+void g(T* src, T* dst, unsigned n)
+{
+  std::uninitialized_move_n(src, n, dst);
+}
-- 
2.53.0

Reply via email to