https://gcc.gnu.org/g:52d702d72afa0ade8edfff144b45539495e4d408
commit r16-4061-g52d702d72afa0ade8edfff144b45539495e4d408 Author: Ben Wu <soggysocks...@gmail.com> Date: Thu Sep 18 17:25:41 2025 -0700 libstdc++: fix element construction in std::deque::emplace [PR118087] In order to emplace a value in the middle of a deque, a temporary was previously constructed directly with __args... in _M_emplace_aux. This would not work since std::deque is allocator-aware and should construct elements with _Alloc_traits::construct instead before the element is moved. Using the suggestion in PR118087, we can define _Temporary_value similar to the one used in std::vector, so the temporary can be constructed with uses-allocator construction. PR libstdc++/118087 libstdc++-v3/ChangeLog: * include/bits/deque.tcc: Use _Temporary_value in _M_emplace_aux. * include/bits/stl_deque.h: Introduce _Temporary_value. * testsuite/23_containers/deque/modifiers/emplace/118087.cc: New test. Reviewed-by: Jonathan Wakely <jwak...@redhat.com> Signed-off-by: Ben Wu <soggysocks...@gmail.com> Diff: --- libstdc++-v3/include/bits/deque.tcc | 11 +++++- libstdc++-v3/include/bits/stl_deque.h | 29 +++++++++++++++ .../deque/modifiers/emplace/118087.cc | 43 ++++++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc index dabb6ec53659..c15b046691ea 100644 --- a/libstdc++-v3/include/bits/deque.tcc +++ b/libstdc++-v3/include/bits/deque.tcc @@ -664,7 +664,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER deque<_Tp, _Alloc>:: _M_emplace_aux(iterator __pos, _Args&&... __args) { - value_type __x_copy(std::forward<_Args>(__args)...); // XXX copy + // We should construct this temporary while the deque is + // in its current state in case something in __args... + // depends on that state before shuffling elements around. + _Temporary_value __tmp(this, std::forward<_Args>(__args)...); #else typename deque<_Tp, _Alloc>::iterator deque<_Tp, _Alloc>:: @@ -695,7 +698,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER __pos = this->_M_impl._M_start + __index; _GLIBCXX_MOVE_BACKWARD3(__pos, __back2, __back1); } - *__pos = _GLIBCXX_MOVE(__x_copy); +#if __cplusplus >= 201103L + *__pos = std::move(__tmp._M_val()); +#else + *__pos = __x_copy; +#endif return __pos; } diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h index 7055641ad4ea..7cc711efca8a 100644 --- a/libstdc++-v3/include/bits/stl_deque.h +++ b/libstdc++-v3/include/bits/stl_deque.h @@ -2163,6 +2163,35 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER iterator _M_insert_aux(iterator __pos, const value_type& __x); #else + struct _Temporary_value + { + template<typename... _Args> + _GLIBCXX20_CONSTEXPR explicit + _Temporary_value(deque* __deque, _Args&&... __args) : _M_this(__deque) + { + _Alloc_traits::construct(_M_this->_M_impl, _M_ptr(), + std::forward<_Args>(__args)...); + } + + _GLIBCXX20_CONSTEXPR + ~_Temporary_value() + { _Alloc_traits::destroy(_M_this->_M_impl, _M_ptr()); } + + _GLIBCXX20_CONSTEXPR value_type& + _M_val() noexcept { return __tmp_val; } + + private: + _GLIBCXX20_CONSTEXPR _Tp* + _M_ptr() noexcept { return std::__addressof(__tmp_val); } + + union + { + _Tp __tmp_val; + }; + + deque* _M_this; + }; + iterator _M_insert_aux(iterator __pos, const value_type& __x) { return _M_emplace_aux(__pos, __x); } diff --git a/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc b/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc new file mode 100644 index 000000000000..3606e8899807 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc @@ -0,0 +1,43 @@ +// { dg-do run { target c++11 } } + +// PR libstdc++/118087 +// std::deque::emplace does not do uses-allocator construction + +#include <deque> +#include <scoped_allocator> +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +template<typename T> +using Alloc = __gnu_test::propagating_allocator<T, true>; + +struct X +{ + using allocator_type = Alloc<int>; + X() { } + X(const X&) { } + X(X&&) { } + X(const allocator_type& a) : alloc(a) { } + X(const X&, const allocator_type& a) : alloc(a) { } + X(X&&, const allocator_type& a) : alloc(a) { } + + X& operator=(const X&) = default; + + allocator_type alloc{-1}; +}; + +int main() +{ + std::deque<X, std::scoped_allocator_adaptor<Alloc<X>>> d(2, Alloc<X>(50)); + VERIFY(d[0].alloc.get_personality() == 50); + VERIFY(d[1].alloc.get_personality() == 50); + + d.emplace(d.begin() + 1); + VERIFY(d[1].alloc.get_personality() == 50); + + d.emplace_front(); + VERIFY(d[0].alloc.get_personality() == 50); + + d.emplace_back(); + VERIFY(d[d.size() - 1].alloc.get_personality() == 50); +}