[gcc r17-2704] libstdc++: Optimize flat_map range insertion for pair-like elements
Patrick Palka
ppalka@gcc.gnu.org
Sat Jul 25 20:13:20 GMT 2026
https://gcc.gnu.org/g:f678da4a9930c8c0e3b4367cd2493ed26321fe54
commit r17-2704-gf678da4a9930c8c0e3b4367cd2493ed26321fe54
Author: Patrick Palka <ppalka@redhat.com>
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 <tkaminsk@redhat.com>
Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
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;
}
More information about the Libstdc++-cvs
mailing list