https://gcc.gnu.org/g:2a36d22ab52d6ffce9a1fcaf7aca83336679e111

commit r15-9211-g2a36d22ab52d6ffce9a1fcaf7aca83336679e111
Author: Patrick Palka <ppa...@redhat.com>
Date:   Fri Apr 4 15:05:09 2025 -0400

    libstdc++: Avoid redundant value_type object in flat_set::emplace [PR119620]
    
    flat_set::emplace (and flat_multiset's) currently unconditionally
    constructs an object outside of the container, but if we're passed
    a value_type object we can and should avoid that.
    
            PR libstdc++/119620
    
    libstdc++-v3/ChangeLog:
    
            * include/std/flat_set (_Flat_set_impl::_M_try_emplace): Split
            out into two overloads, one taking at least one argument and one
            taking zero arguments.  Turn __k into an auto&& reference bound
            to __arg if it's already a value_type and otherwise bound to a
            lifetime-extended value_type temporary.
            * testsuite/23_containers/flat_multiset/1.cc (test08): New test.
            * testsuite/23_containers/flat_set/1.cc (test08): New test.
    
    Reviewed-by: Tomasz KamiƄski <tkami...@redhat.com>
    Reviewed-by: Jonathan Wakely <jwak...@redhat.com>

Diff:
---
 libstdc++-v3/include/std/flat_set                  | 20 ++++++++++++++++----
 .../testsuite/23_containers/flat_multiset/1.cc     | 22 ++++++++++++++++++++++
 libstdc++-v3/testsuite/23_containers/flat_set/1.cc | 20 ++++++++++++++++++++
 3 files changed, 58 insertions(+), 4 deletions(-)

diff --git a/libstdc++-v3/include/std/flat_set 
b/libstdc++-v3/include/std/flat_set
index a7b0b8aef151..3e15d1af4162 100644
--- a/libstdc++-v3/include/std/flat_set
+++ b/libstdc++-v3/include/std/flat_set
@@ -350,12 +350,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return _M_cont.max_size(); }
 
       // modifiers
-      template<typename... _Args>
+      template<typename _Arg, typename... _Args>
        pair<iterator, bool>
-       _M_try_emplace(optional<const_iterator> __hint, _Args&&... __args)
+       _M_try_emplace(optional<const_iterator> __hint, _Arg&& __arg, 
_Args&&... __args)
        {
          // TODO: Simplify and audit the hint handling.
-         value_type __k(std::forward<_Args>(__args)...);
+         auto&& __k = [&] -> decltype(auto) {
+           if constexpr (sizeof...(_Args) == 0
+                         && same_as<remove_cvref_t<_Arg>, value_type>)
+             return std::forward<_Arg>(__arg);
+           else
+             return value_type(std::forward<_Arg>(__arg),
+                               std::forward<_Args>(__args)...);
+         }();
          typename container_type::iterator __it;
          int __r = -1, __s = -1;
          if (__hint.has_value()
@@ -397,11 +404,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
              return {__it, false};
 
          auto __guard = _M_make_clear_guard();
-         __it = _M_cont.insert(__it, std::move(__k));
+         __it = _M_cont.insert(__it, std::forward<decltype(__k)>(__k));
          __guard._M_disable();
          return {__it, true};
        }
 
+      template<typename... _Args>
+       pair<iterator, bool>
+       _M_try_emplace(optional<const_iterator> __hint)
+       { return _M_try_emplace(__hint, value_type()); }
+
       template<typename... _Args>
        requires is_constructible_v<value_type, _Args...>
        __emplace_result_t
diff --git a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc 
b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
index dc3cecd9720c..7d9a33c6b000 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
@@ -214,6 +214,27 @@ void test07()
 #endif
 }
 
+void
+test08()
+{
+  // PR libstdc++/119620 -- flat_set::emplace always constructs element on the 
stack
+  static int copy_counter;
+  struct A {
+    A() { }
+    A(const A&) { ++copy_counter; }
+    A& operator=(const A&) { ++copy_counter; return *this; }
+    auto operator<=>(const A&) const = default;
+  };
+  std::vector<A> v;
+  v.reserve(2);
+  std::flat_multiset<A> s(std::move(v));
+  A a;
+  s.emplace(a);
+  VERIFY( copy_counter == 1 );
+  s.emplace(a);
+  VERIFY( copy_counter == 2 );
+}
+
 int
 main()
 {
@@ -225,4 +246,5 @@ main()
   test05();
   test06();
   test07();
+  test08();
 }
diff --git a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc 
b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
index 90f5855859f7..ed24fab785b5 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
@@ -229,6 +229,25 @@ void test07()
 #endif
 }
 
+void
+test08()
+{
+  // PR libstdc++/119620 -- flat_set::emplace always constructs element on the 
stack
+  static int copy_counter;
+  struct A {
+    A() { }
+    A(const A&) { ++copy_counter; }
+    A& operator=(const A&) { ++copy_counter; return *this; }
+    auto operator<=>(const A&) const = default;
+  };
+  std::flat_set<A> s;
+  A a;
+  s.emplace(a);
+  VERIFY( copy_counter == 1 );
+  s.emplace(a);
+  VERIFY( copy_counter == 1 );
+}
+
 int
 main()
 {
@@ -240,4 +259,5 @@ main()
   test05();
   test06();
   test07();
+  test08();
 }

Reply via email to