On Thu, 8 Oct 2026 at 11:19, Tomasz Kamiński <[email protected]> wrote:
>
> Implements LWG4121, "ranges::to constructs associative containers
> via c.emplace(c.end(), *it)".
>
> Using emplace_hint(it, arg) provides support for associative container
> without from_range constructor, from which emplace(it, arg) constructrs
> elements from it, arg. All statndard sequence containers that provides
> emplace(it, arg) overload, provides push_back/emplace_back and are
> handled by eariel branches.
>
> libstdc++-v3/ChangeLog:
>
> * include/std/ranges (ranges::to): Replace emplace(__c.end(), *__it)
> with emplace_hint(__c.end(), *__it)
> * testsuite/std/ranges/conv/1.cc: Update tests accordingly.
> ---
> Testing on x86_64-linux. *ranges* test already passed in all
> standard modes. OK for trunk when all test passes?
OK
>
> libstdc++-v3/include/std/ranges | 8 +--
> libstdc++-v3/testsuite/std/ranges/conv/1.cc | 60 +++++++++++----------
> 2 files changed, 36 insertions(+), 32 deletions(-)
>
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index 37f048e3d0d..5963647c378 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -9563,7 +9563,9 @@ namespace __detail
>
> __c.reserve(static_cast<range_size_t<_Cont>>(ranges::size(__r)));
> // _GLIBCXX_RESOLVE_LIB_DEFECTS
> // 4016. container-insertable checks do not match what
> - // container-inserter does
> + // container-inserter does
> + // 4121. ranges::to constructs associative containers
> + // via c.emplace(c.end(), *it)
> auto __it = ranges::begin(__r);
> const auto __sent = ranges::end(__r);
> while (__it != __sent)
> @@ -9572,8 +9574,8 @@ namespace __detail
> __c.emplace_back(*__it);
> else if constexpr (requires { __c.push_back(*__it); })
> __c.push_back(*__it);
> - else if constexpr (requires { __c.emplace(__c.end(),
> *__it); })
> - __c.emplace(__c.end(), *__it);
> + else if constexpr (requires { __c.emplace_hint(__c.end(),
> *__it); })
> + __c.emplace_hint(__c.end(), *__it);
> else
> __c.insert(__c.end(), *__it);
> ++__it;
> diff --git a/libstdc++-v3/testsuite/std/ranges/conv/1.cc
> b/libstdc++-v3/testsuite/std/ranges/conv/1.cc
> index 2caa1b83f30..14b52f6dd3c 100644
> --- a/libstdc++-v3/testsuite/std/ranges/conv/1.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/conv/1.cc
> @@ -9,6 +9,7 @@
> #include <list>
> #include <forward_list>
> #include <map>
> +#include <set>
> #include <testsuite_hooks.h>
> #include <testsuite_allocator.h>
> #include <testsuite_iterators.h>
> @@ -204,13 +205,13 @@ test_2_1_3()
> VERIFY( c2.c.get_allocator() == Alloc(78) );
> }
>
> -enum AppendKind { None, EmplaceBack, PushBack, Emplace, Insert };
> +enum AppendKind { None, EmplaceBack, PushBack, EmplaceHint, Insert };
>
> template<typename C, AppendKind Kind>
> struct Cont4
> {
> // Only support construction with no args or an allocator.
> - // This forces the use of either emplace_back, push_back, emplace or
> insert.
> + // This forces the use of either emplace_back, push_back, emplace_hint or
> insert.
> Cont4() { }
> Cont4(typename C::allocator_type a) : c(a) { }
>
> @@ -235,13 +236,13 @@ struct Cont4
> }
>
> template<typename T>
> - requires (Kind <= Emplace)
> - && requires(C& c, T&& t) { c.emplace(c.end(), std::forward<T>(t)); }
> + requires (Kind <= EmplaceHint)
> + && requires(C& c, T&& t) { c.emplace_hint(c.end(), std::forward<T>(t)); }
> void
> - emplace(typename C::iterator pos, T&& t)
> + emplace_hint(typename C::iterator pos, T&& t)
> {
> - kind = Emplace;
> - c.emplace(pos, std::forward<T>(t));
> + kind = EmplaceHint;
> + c.emplace_hint(pos, std::forward<T>(t));
> }
>
> template<typename T>
> @@ -287,6 +288,7 @@ test_2_1_4()
> using Alloc2 = __gnu_test::uneq_allocator<short>;
> using V = std::vector<int, Alloc>;
> using List = std::list<short, Alloc2>;
> + using Set = std::set<int>;
>
> std::list<unsigned> l{1u, 2u, 3u};
> std::list<long> l2{4l, 5l, 6l};
> @@ -337,30 +339,30 @@ test_2_1_4()
> VERIFY( c6.kind == PushBack );
> VERIFY( ! c6.used_reserve );
>
> - // use vector::emplace and vector::reserve
> - auto c7 = std::ranges::to<Cont4<V, Emplace>>(l);
> - static_assert(std::is_same_v<decltype(c7), Cont4<V, Emplace>>);
> - VERIFY( c7.c == V(l.begin(), l.end()) );
> - VERIFY( c7.kind == Emplace );
> - VERIFY( c7.used_reserve );
> -
> - // use vector::emplace and vector::reserve
> - auto c8 = std::ranges::to<Cont4<V, Emplace>>(l2, Alloc(78));
> - static_assert(std::is_same_v<decltype(c8), Cont4<V, Emplace>>);
> - VERIFY( c8.c == V(l2.begin(), l2.end()) );
> - VERIFY( c8.c.get_allocator() == Alloc(78) );
> - VERIFY( c8.kind == Emplace );
> - VERIFY( c8.used_reserve );
> -
> - // use list::emplace
> - auto c9 = std::ranges::to<Cont4<List, Emplace>>(c.c, Alloc2(99));
> - static_assert(std::is_same_v<decltype(c9), Cont4<List, Emplace>>);
> - VERIFY( c9.c == List(l.begin(), l.end()) );
> - VERIFY( c9.c.get_allocator() == Alloc(99) );
> - VERIFY( c9.kind == Emplace );
> + // use list::emplace_back
> + auto c7 = std::ranges::to<Cont4<List, EmplaceBack>>(c.c, Alloc2(99));
> + static_assert(std::is_same_v<decltype(c7), Cont4<List, EmplaceBack>>);
> + VERIFY( c7.c == List(l.begin(), l.end()) );
> + VERIFY( c7.c.get_allocator() == Alloc(99) );
> + VERIFY( c7.kind == EmplaceBack );
> + VERIFY( ! c7.used_reserve );
> +
> + // use set::emplace_hint
> + auto c8 = std::ranges::to<Cont4<Set, EmplaceHint>>(l);
> + static_assert(std::is_same_v<decltype(c8), Cont4<Set, EmplaceHint>>);
> + VERIFY( c8.c == Set(l.begin(), l.end()) );
> + VERIFY( c8.kind == EmplaceHint );
> + VERIFY( ! c8.used_reserve );
> +
> + // use set::emplace_hint
> + auto c9 = std::ranges::to<Cont4<Set, EmplaceHint>>(l2, Alloc(78));
> + static_assert(std::is_same_v<decltype(c9), Cont4<Set, EmplaceHint>>);
> + VERIFY( c9.c == Set(l2.begin(), l2.end()) );
> + VERIFY( c9.c.get_allocator() == Alloc(78) );
> + VERIFY( c9.kind == EmplaceHint );
> VERIFY( ! c9.used_reserve );
>
> - // use vector::insert and vector::reserve
> + // use vector::insert and vector::reserve
> auto c10 = std::ranges::to<Cont4<V, Insert>>(l);
> static_assert(std::is_same_v<decltype(c10), Cont4<V, Insert>>);
> VERIFY( c10.c == V(l.begin(), l.end()) );
> --
> 2.55.0
>