[gcc r17-2704] libstdc++: Optimize flat_map range insertion for pair-like elements

Patrick Palka via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <20260725201320.2A5F64BA543C__42945.9638356761$1785010408$gmane$org@sourceware.org>
https://gcc.gnu.org/g:f678da4a9930c8c0e3b4367cd2493ed26321fe54

commit r17-2704-gf678da4a9930c8c0e3b4367cd2493ed26321fe54
Author: Patrick Palka <[email protected]>
Date:   Sat Jul 25 16:12:13 2026 -0400

    libstdc++: Optimize flat_map range insertion for pair-like elements
    
    When inserting a range of pair-like elements we can avoid constructing a
    value_type (i.e. pair) temporary and instead obtain the corresponding key
    and value directly from *__first.
    
    This came up when looking at LWG 4499 for flat_set::insert_range (which
    I think we already optimally implement) but it prompted me to look at
    flat_map::insert_range during which I noticed this extra std::move.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/flat_map (flat_map::_M_insert): Avoid constructing
            value_type temporary when the iterator already has pair-like
            elements.
            * testsuite/23_containers/flat_map/1.cc (test14): New test.
    
    Reviewed-by: Tomasz Kamiński <[email protected]>
    Reviewed-by: Jonathan Wakely <[email protected]>

Diff:
---
 libstdc++-v3/include/std/flat_map                  | 18 +++++++----
 libstdc++-v3/testsuite/23_containers/flat_map/1.cc | 35 ++++++++++++++++++++++
 2 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/libstdc++-v3/include/std/flat_map b/libstdc++-v3/include/std/flat_map
index b82d41b4f5e1..ad5fddfe29e1 100644
--- a/libstdc++-v3/include/std/flat_map
+++ b/libstdc++-v3/include/std/flat_map
@@ -635,12 +635,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  auto __guard = _M_make_clear_guard();
 	  auto __n = size();
+	  using __ref = iter_reference_t<_Iter>;
 	  for (; __first != __last; ++__first)
-	    {
-	      value_type __value = *__first;
-	      _M_cont.keys.emplace_back(std::move(__value.first));
-	      _M_cont.values.emplace_back(std::move(__value.second));
-	    }
+	    if constexpr (__pair_like<__ref>)
+	      {
+		__ref __value = *__first;
+		_M_cont.keys.emplace_back(std::get<0>(std::forward<__ref>(__value)));
+		_M_cont.values.emplace_back(std::get<1>(std::forward<__ref>(__value)));
+	      }
+	    else
+	      {
+		value_type __value = *__first;
+		_M_cont.keys.emplace_back(std::move(__value.first));
+		_M_cont.values.emplace_back(std::move(__value.second));
+	      }
 	  auto __zv = views::zip(_M_cont.keys, _M_cont.values);
 	  if (__is_sorted)
 	    _GLIBCXX_DEBUG_ASSERT(ranges::is_sorted(__zv.begin() + __n, __zv.end(),
diff --git a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
index 0cd06b72e96c..d53e56a1b717 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
@@ -405,6 +405,39 @@ test13()
   VERIFY( std::ranges::equal(s.values(), (int[]){3, 1, 2}) );
 }
 
+void
+test14()
+{
+  // Verify optimal number of moves in flat_map::insert_range for sorted_unique
+  static int moves;
+  struct counter
+  {
+    int val;
+    constexpr counter() = default;
+    constexpr counter(int v) : val(v) {}
+    constexpr counter(const counter&) = default;
+    constexpr counter(counter&& o) noexcept : val(o.val) { ++moves; }
+    constexpr counter& operator=(const counter& o) = default;
+    constexpr counter& operator=(counter&& o) noexcept {
+      val = o.val;
+      ++moves;
+      return *this;
+    }
+    constexpr bool operator==(const counter&) const = default;
+    constexpr auto operator<=>(const counter& o) const = default;
+  };
+
+  std::flat_map<counter, counter> m;
+  std::pair<counter, counter> r[] = {
+    {counter(1), counter(10)},
+    {counter(2), counter(20)},
+    {counter(3), counter(30)},
+  };
+  moves = 0;
+  m.insert_range(std::sorted_unique, std::views::as_rvalue(r));
+  VERIFY( moves == 6 );
+}
+
 void
 test()
 {
@@ -425,6 +458,7 @@ test()
   test11<throwing_vector, std::vector>();
   test12();
   test13();
+  test14();
 }
 
 constexpr
@@ -446,6 +480,7 @@ test_constexpr()
   // test11() is non-constexpr
   test12();
   // test13() is non-constexpr
+  // test14() is non-constexpr
   return true;
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.