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
From 6c8e01a31be057aab2dd26cccf2f147d6299cb06 Mon Sep 17 00:00:00 2001
From: benwu25 <[email protected]>
Date: Thu, 18 Sep 2025 17:25:41 -0700
Subject: [PATCH] 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.

---
 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

Reply via email to