Thanks for the patch. N.B. all patches for libstdc++ should be CC'd to [email protected] as well as gcc-patches. (I've added libstdc++ to the CC list now).
On Fri, 19 Sept 2025 at 02:52, Ben Wu <[email protected]> wrote: > > Bootstrapped and tested with check-target-libstdc++-v3 on x86_64-pc-linux-gnu. > > Could someone help review and commit? > > For applying, please use the attached patch file since my email client has > not preserved > the tabs. > > Thanks, > -Ben > > -- 8< -- > > 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. > > --- > libstdc++-v3/include/bits/deque.tcc | 9 ++- > libstdc++-v3/include/bits/stl_deque.h | 29 +++++++++ > .../deque/modifiers/emplace/118087.cc | 62 +++++++++++++++++++ > 3 files changed, 99 insertions(+), 1 deletion(-) > create mode 100644 > libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc > > diff --git a/libstdc++-v3/include/bits/deque.tcc > b/libstdc++-v3/include/bits/deque.tcc > index dabb6ec5365..95588ffd860 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); > } > +#if __cplusplus >= 201103L > + *__pos = _GLIBCXX_MOVE(__tmp._M_val()); > +#else > *__pos = _GLIBCXX_MOVE(__x_copy); > +#endif > return __pos; > } > > diff --git a/libstdc++-v3/include/bits/stl_deque.h > b/libstdc++-v3/include/bits/stl_deque.h > index 7055641ad4e..7cc711efca8 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 00000000000..0dc37491824 > --- /dev/null > +++ b/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc > @@ -0,0 +1,62 @@ > +// { dg-do run { target c++11 } } > + > +#include <testsuite_hooks.h> > +#include <deque> > +#include <scoped_allocator> > + > +template<typename T> > +struct Alloc > +{ > + Alloc(int i) : id(i) { } > + template<typename U> > + Alloc(const Alloc<U>& a) : id(a.id) { } > + > + using value_type = T; > + using propagate_on_container_copy_assignment = std::true_type; > + using propagate_on_container_move_assignment = std::true_type; > + using propagate_on_container_swap = std::true_type; > + > + using Base = std::allocator<T>; > + > + T* allocate(std::size_t n) { return Base().allocate(n); } > + void deallocate(T* p, std::size_t n) { return Base().deallocate(p, n); } > + > + int id; > + > + bool operator==(const Alloc& a) const { return id == a.id; } > + bool operator!=(const Alloc& a) const { return id != a.id; } > +}; > + > +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.id == 50); > + VERIFY(d[1].alloc.id == 50); > + > + d.emplace(d.begin() + 1); > + VERIFY(d[1].alloc.id == 50); > + > + d.emplace_front(); > + VERIFY(d[0].alloc.id == 50); > + > + d.emplace_back(); > + VERIFY(d[d.size() - 1].alloc.id == 50); > + > + return 0; > +} > -- > 2.43.0
