On Wed, May 27, 2026 at 6:24 PM Patrick Palka <[email protected]> wrote:
> This assignment operator was not being brought in from the private base
> class causing assignments from {...} to be inefficiently treated as
> construction + move assignment.
>
> libstdc++-v3/ChangeLog:
>
> * include/std/flat_map (flat_map): Bring in operator= from
> _Flat_map_base.
> (flat_multimap): Likewise.
> * include/std/flat_set (flat_set): Bring in operator= from
> _Flat_set_base.
> (flat_multiset): Likewise.
> * testsuite/23_containers/flat_map/1.cc (test11): Simplify by
> using = {...}.
> (test12): New test.
> * testsuite/23_containers/flat_multimap/1.cc (test10): Simplify
> by using = {...}.
> (test11): New test.
> * testsuite/23_containers/flat_multiset/1.cc (test10): Simplify
> by using = {...}.
> (test11): New test.
> * testsuite/23_containers/flat_set/1.cc (test10): Simplify by
> using = {...}.
> (test11): New test.
> ---
>
Nice catch. The changes looks good to me.
> libstdc++-v3/include/std/flat_map | 10 ++++++++
> libstdc++-v3/include/std/flat_set | 10 ++++++++
> .../testsuite/23_containers/flat_map/1.cc | 24 +++++++++++++-----
> .../23_containers/flat_multimap/1.cc | 24 +++++++++++++-----
> .../23_containers/flat_multiset/1.cc | 25 ++++++++++++++-----
> .../testsuite/23_containers/flat_set/1.cc | 25 ++++++++++++++-----
> 6 files changed, 94 insertions(+), 24 deletions(-)
>
> diff --git a/libstdc++-v3/include/std/flat_map
> b/libstdc++-v3/include/std/flat_map
> index 18b2d24bb1f3..ce0039d71d1f 100644
> --- a/libstdc++-v3/include/std/flat_map
> +++ b/libstdc++-v3/include/std/flat_map
> @@ -1276,6 +1276,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> // constructors
> using _Impl::_Impl;
>
> + // operator=(initializer_list<value_type>)
> + // Although this also brings in the base move/copy assignment
> operators,
> + // they will be hidden by our synthesized assignment operators.
> + using _Impl::operator=;
> +
> // iterators
> using _Impl::begin;
> using _Impl::end;
> @@ -1629,6 +1634,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> // constructors
> using _Impl::_Impl;
>
> + // operator=(initializer_list<value_type>)
> + // Although this also brings in the base move/copy assignment
> operators,
> + // they will be hidden by our synthesized assignment operators.
> + using _Impl::operator=;
> +
> // iterators
> using _Impl::begin;
> using _Impl::end;
> diff --git a/libstdc++-v3/include/std/flat_set
> b/libstdc++-v3/include/std/flat_set
> index f583d352875e..9fe7e48400e2 100644
> --- a/libstdc++-v3/include/std/flat_set
> +++ b/libstdc++-v3/include/std/flat_set
> @@ -968,6 +968,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> // constructors
> using _Impl::_Impl;
>
> + // operator=(initializer_list<value_type>)
> + // Although this also brings in the base move/copy assignment
> operators,
> + // they will be hidden by our synthesized assignment operators.
> + using _Impl::operator=;
> +
> // iterators
> using _Impl::begin;
> using _Impl::end;
> @@ -1116,6 +1121,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> // constructors
> using _Impl::_Impl;
>
> + // operator=(initializer_list<value_type>)
> + // Although this also brings in the base move/copy assignment
> operators,
> + // they will be hidden by our synthesized assignment operators.
> + using _Impl::operator=;
> +
> // iterators
> using _Impl::begin;
> using _Impl::end;
> diff --git a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
> b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
> index b4d84d30a1ed..1d98a5abae56 100644
> --- a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
> +++ b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
> @@ -351,8 +351,7 @@ test11()
> }
>
> // Verify invariant preservation upon throwing move assignment.
> - source.clear();
> - source.insert({{1, 100}, {2, 200}});
> + source = {{1, 100}, {2, 200}};
> flat_map target;
> target.insert({{3, 300}, {4, 400}});
> try
> @@ -367,10 +366,8 @@ test11()
> }
>
> // Verify invariant preservation upon throwing swap.
> - source.clear();
> - source.insert({{1, 100}, {2, 200}});
> - target.clear();
> - target.insert({{3, 300}, {4, 400}});
> + source = {{1, 100}, {2, 200}};
> + target = {{3, 300}, {4, 400}};
> try
> {
> source.swap(target);
> @@ -395,6 +392,19 @@ test12()
> VERIFY( std::ranges::equal(m.values(), (int[]){100, 200}) );
> }
>
> +constexpr
> +void
> +test13()
> +{
> + // Verify usability of flat_map::operator=(initializer_list).
> + throwing_vector<int>::throw_on_move = true;
> + std::flat_map<int, int, std::less<int>, throwing_vector<int>> s;
> + std::initializer_list<std::pair<int, int>> il = {{2, 1}, {3, 2}, {1,
> 3}};
> + s = il;
> + VERIFY( std::ranges::equal(s.keys(), (int[]){1, 2, 3}) );
> + VERIFY( std::ranges::equal(s.values(), (int[]){3, 1, 2}) );
> +}
> +
> void
> test()
> {
> @@ -414,6 +424,7 @@ test()
> test11<std::vector, throwing_vector>();
> test11<throwing_vector, std::vector>();
> test12();
> + test13();
> }
>
> constexpr
> @@ -434,6 +445,7 @@ test_constexpr()
> #endif
> // test11() is non-constexpr
> test12();
> + test13();
> return true;
> }
>
> diff --git a/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
> b/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
> index 0e81ef8b11e9..c892aace6360 100644
> --- a/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
> +++ b/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
> @@ -304,8 +304,7 @@ test10()
> }
>
> // Verify invariant preservation upon throwing move assignment.
> - source.clear();
> - source.insert({{1, 100}, {2, 200}});
> + source = {{1, 100}, {2, 200}};
> flat_multimap target;
> target.insert({{3, 300}, {4, 400}});
> try
> @@ -320,10 +319,8 @@ test10()
> }
>
> // Verify invariant preservation upon throwing swap.
> - source.clear();
> - source.insert({{1, 100}, {2, 200}});
> - target.clear();
> - target.insert({{3, 300}, {4, 400}});
> + source = {{1, 100}, {2, 200}};
> + target = {{3, 300}, {4, 400}};
> try
> {
> source.swap(target);
> @@ -348,6 +345,19 @@ test11()
> VERIFY( std::ranges::equal(m.values(), (int[]){100, 200}) );
> }
>
> +constexpr
> +void
> +test12()
> +{
> + // Verify usability of flat_multimap::operator=(initializer_list).
> + throwing_vector<int>::throw_on_move = true;
> + std::flat_multimap<int, int, std::less<int>, throwing_vector<int>> s;
> + std::initializer_list<std::pair<int, int>> il = {{2, 1}, {3, 2}, {1,
> 3}};
> + s = il;
> + VERIFY( std::ranges::equal(s.keys(), (int[]){1, 2, 3}) );
> + VERIFY( std::ranges::equal(s.values(), (int[]){3, 1, 2}) );
> +}
> +
> void
> test()
> {
> @@ -365,6 +375,7 @@ test()
> test10<std::vector, throwing_vector>();
> test10<throwing_vector, std::vector>();
> test11();
> + test12();
> }
>
> constexpr
> @@ -381,6 +392,7 @@ test_constexpr()
> test09();
> // test11() is non-constexpr
> test11();
> + test12();
> return true;
> }
>
> diff --git a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
> b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
> index a60c74069633..cf668089b3e4 100644
> --- a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
> +++ b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
> @@ -289,6 +289,8 @@ struct throwing_vector : std::vector<T>
> throw std::runtime_error("move assign");
> return *this;
> }
> +
> + using std::vector<int>::operator=;
> };
>
> void
> @@ -312,8 +314,7 @@ test10()
> }
>
> // Verify invariant preservation upon throwing move assignment.
> - source.clear();
> - source.insert({1, 2});
> + source = {1, 2};
> flat_multiset target = {3, 4};
> try
> {
> @@ -327,10 +328,8 @@ test10()
> }
>
> // Verify invariant preservation upon throwing swap.
> - source.clear();
> - source.insert({1, 2});
> - target.clear();
> - target.insert({3, 4});
> + source = {1, 2};
> + target = {3, 4};
> try
> {
> source.swap(target);
> @@ -355,6 +354,18 @@ test11()
> VERIFY( std::ranges::equal(m, s) );
> }
>
> +constexpr
> +void
> +test12()
> +{
> + // Verify usability of flat_multiset::operator=(initializer_list).
> + throwing_vector<int>::throw_on_move = true;
> + std::flat_multiset<int, std::less<int>, throwing_vector<int>> s;
> + std::initializer_list<int> il = {2, 3, 1};
> + s = il;
> + VERIFY( std::ranges::equal(s, (int[]){1, 2, 3}) );
> +}
> +
> void
> test()
> {
> @@ -370,6 +381,7 @@ test()
> test09();
> test10();
> test11();
> + test12();
> }
>
> constexpr
> @@ -386,6 +398,7 @@ test_constexpr()
> test09();
> // test10() is non-constexpr
> test11();
> + test12();
> return true;
> }
>
> diff --git a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
> b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
> index 16b21f9181ea..a46343fdbfbd 100644
> --- a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
> +++ b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
> @@ -301,6 +301,8 @@ struct throwing_vector : std::vector<T>
> throw std::runtime_error("move assign");
> return *this;
> }
> +
> + using std::vector<int>::operator=;
> };
>
> void
> @@ -324,8 +326,7 @@ test10()
> }
>
> // Verify invariant preservation upon throwing move assignment.
> - source.clear();
> - source.insert({1, 2});
> + source = {1, 2};
> flat_set target = {3, 4};
> try
> {
> @@ -339,10 +340,8 @@ test10()
> }
>
> // Verify invariant preservation upon throwing swap.
> - source.clear();
> - source.insert({1, 2});
> - target.clear();
> - target.insert({3, 4});
> + source = {1, 2};
> + target = {3, 4};
> try
> {
> source.swap(target);
> @@ -367,6 +366,18 @@ test11()
> VERIFY( std::ranges::equal(m, s) );
> }
>
> +constexpr
> +void
> +test12()
> +{
> + // Verify usability of flat_set::operator=(initializer_list).
> + throwing_vector<int>::throw_on_move = true;
> + std::flat_set<int, std::less<int>, throwing_vector<int>> s;
> + std::initializer_list<int> il = {2, 3, 1};
> + s = il;
> + VERIFY( std::ranges::equal(s, (int[]){1, 2, 3}) );
> +}
> +
> void
> test()
> {
> @@ -382,6 +393,7 @@ test()
> test09();
> test10();
> test11();
> + test12();
> }
>
> constexpr
> @@ -398,6 +410,7 @@ test_constexpr()
> test09();
> // test10() is non-constexpr
> test11();
> + test12();
> return true;
> }
>
> --
> 2.54.0.rc1.54.g60f07c4f5c
>
>