On Fri, 17 Jul 2026, 19:02 Patrick Palka, <[email protected]> wrote:
> On Mon, 13 Jul 2026, Tomasz Kaminski wrote:
>
> >
> >
> > On Mon, Jul 13, 2026 at 5:22 PM Patrick Palka <[email protected]> wrote:
> > Tested on x86_64-pc-linux-gnu, does this look OK for trunk and
> perhaps 16?
> > I'm not sure if it's worth avoiding dereferencing *__first twice
> in this
> > special case, but I figured we might as well?
> >
> > -- >8 --
> >
> > When inserting a range of pair-like elements we can avoid
> constructing a
> > value_type temporary and instead obtain the corresponding key and
> value
> > to insert directly from *__first.
> >
> > libstdc++-v3/ChangeLog:
> >
> > * include/std/flat_map (flat_map::_M_insert): Avoid
> constructing
> > value_type temporary when the iterator already has
> pair-like
> > elements.
> > ---
> > libstdc++-v3/include/std/flat_map | 17 ++++++++++++++---
> > 1 file changed, 14 insertions(+), 3 deletions(-)
> >
> > LGTM, only small suggestions.
> >
> > diff --git a/libstdc++-v3/include/std/flat_map
> b/libstdc++-v3/include/std/flat_map
> > index b82d41b4f5e1..1815ced51225 100644
> > --- a/libstdc++-v3/include/std/flat_map
> > +++ b/libstdc++-v3/include/std/flat_map
> > @@ -637,9 +637,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > auto __n = size();
> > for (; __first != __last; ++__first)
> > {
> >
> > I do not think we need this braces for nesting.
>
> Fixed.
>
> > - 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<iter_reference_t<_Iter>>)
> > + {
> > + auto&& __value = *__first;
> > + _M_cont.keys.emplace_back
> > +
> (std::get<0>(std::forward<decltype(__value)>(__value)));
> > + _M_cont.values.emplace_back
> > +
> (std::get<1>(std::forward<decltype(__value)>(__value)));
> >
> > // Maybe we could alias iter_reference_t<_Iter> and replace the
> decltypes(__value) and auto&&?
>
> Sounds good. Avoiding temporary materialization and an intermediate
> reference should be cheaper with -O0.
>
> > + }
> > + 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)
> > --
> > 2.55.0.122.gf85a7e6620
> >
> > Do you think it will be worth to have a test that will count number of
> moves, when
> > we pass sourted_unique flag is passed?
>
> Sure. Like so?
>
> Changes in v2:
> - Addresses Tomasz's comments
> - Clarified relevance of LWG 4499
>
OK for trunk, thanks
> -- >8 --
>
> Subject: [PATCH v2] 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 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 implement, with no extra std::move) 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.
> ---
> libstdc++-v3/include/std/flat_map | 18 +++++++---
> .../testsuite/23_containers/flat_map/1.cc | 34 +++++++++++++++++++
> 2 files changed, 47 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..0e539af3e5f2 100644
> --- a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
> +++ b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
> @@ -405,6 +405,38 @@ 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> s[] = {
> + {counter(1), counter(10)},
> + {counter(2), counter(20)}
> + };
> + moves = 0;
> + m.insert_range(std::sorted_unique, std::views::as_rvalue(s));
> + VERIFY( moves == 4 );
> +}
> +
> void
> test()
> {
> @@ -425,6 +457,7 @@ test()
> test11<throwing_vector, std::vector>();
> test12();
> test13();
> + test14();
> }
>
> constexpr
> @@ -446,6 +479,7 @@ test_constexpr()
> // test11() is non-constexpr
> test12();
> // test13() is non-constexpr
> + // test14() is non-constexpr
> return true;
> }
>
> --
> 2.55.0.178.gd35c5399e3
>