libstdc++
scoped_allocator
Go to the documentation of this file.
1 // <scoped_allocator> -*- C++ -*-
2 
3 // Copyright (C) 2011-2025 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10 
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19 
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
24 
25 /** @file include/scoped_allocator
26  * This is a Standard C++ Library header.
27  */
28 
29 #ifndef _SCOPED_ALLOCATOR
30 #define _SCOPED_ALLOCATOR 1
31 
32 #ifdef _GLIBCXX_SYSHDR
33 #pragma GCC system_header
34 #endif
35 
36 #if __cplusplus < 201103L
37 # include <bits/c++0x_warning.h>
38 #else
39 
40 #include <tuple>
41 #include <bits/alloc_traits.h>
42 #include <bits/stl_pair.h>
43 #include <bits/uses_allocator.h>
44 #if __cplusplus > 201703L
45 # include <bits/uses_allocator_args.h>
46 #endif
47 
48 #define __glibcxx_want_allocator_traits_is_always_equal
49 #include <bits/version.h>
50 
51 namespace std _GLIBCXX_VISIBILITY(default)
52 {
53 _GLIBCXX_BEGIN_NAMESPACE_VERSION
54 
55  /**
56  * @addtogroup allocators
57  * @{
58  */
59 
60  template<typename _OuterAlloc, typename... _InnerAllocs>
61  class scoped_allocator_adaptor;
62 
63  /// @cond undocumented
64 
65  template<typename _Alloc>
66  using __outer_allocator_t
67  = decltype(std::declval<_Alloc>().outer_allocator());
68 
69  template<typename _Alloc, typename = void>
70  struct __outermost_type
71  {
72  using type = _Alloc;
73  static type& _S_outermost(_Alloc& __a) noexcept { return __a; }
74  };
75 
76  template<typename _Alloc>
77  struct __outermost_type<_Alloc, __void_t<__outer_allocator_t<_Alloc>>>
78  : __outermost_type<
79  typename remove_reference<__outer_allocator_t<_Alloc>>::type
80  >
81  {
82  using __base = __outermost_type<
83  typename remove_reference<__outer_allocator_t<_Alloc>>::type
84  >;
85 
86  static typename __base::type&
87  _S_outermost(_Alloc& __a) noexcept
88  { return __base::_S_outermost(__a.outer_allocator()); }
89  };
90 
91  // Implementation of the OUTERMOST pseudofunction
92  template<typename _Alloc>
93  inline typename __outermost_type<_Alloc>::type&
94  __outermost(_Alloc& __a)
95  { return __outermost_type<_Alloc>::_S_outermost(__a); }
96 
97  template<typename...>
98  struct __inner_type_impl;
99 
100  template<typename _Outer>
101  struct __inner_type_impl<_Outer>
102  {
103  typedef scoped_allocator_adaptor<_Outer> __type;
104 
105  __inner_type_impl() = default;
106  __inner_type_impl(const __inner_type_impl&) = default;
107  __inner_type_impl(__inner_type_impl&&) = default;
108  __inner_type_impl& operator=(const __inner_type_impl&) = default;
109  __inner_type_impl& operator=(__inner_type_impl&&) = default;
110 
111  template<typename _Alloc>
112  __inner_type_impl(const __inner_type_impl<_Alloc>&) noexcept
113  { }
114 
115  template<typename _Alloc>
116  __inner_type_impl(__inner_type_impl<_Alloc>&&) noexcept
117  { }
118 
119  __type&
120  _M_get(__type* __p) noexcept { return *__p; }
121 
122  const __type&
123  _M_get(const __type* __p) const noexcept { return *__p; }
124 
125  tuple<>
126  _M_tie() const noexcept { return tuple<>(); }
127 
128  bool
129  operator==(const __inner_type_impl&) const noexcept
130  { return true; }
131  };
132 
133  template<typename _Outer, typename _InnerHead, typename... _InnerTail>
134  struct __inner_type_impl<_Outer, _InnerHead, _InnerTail...>
135  {
136  typedef scoped_allocator_adaptor<_InnerHead, _InnerTail...> __type;
137 
138  __inner_type_impl() = default;
139  __inner_type_impl(const __inner_type_impl&) = default;
140  __inner_type_impl(__inner_type_impl&&) = default;
141  __inner_type_impl& operator=(const __inner_type_impl&) = default;
142  __inner_type_impl& operator=(__inner_type_impl&&) = default;
143 
144  template<typename... _Allocs>
145  __inner_type_impl(const __inner_type_impl<_Allocs...>& __other) noexcept
146  : _M_inner(__other._M_inner) { }
147 
148  template<typename... _Allocs>
149  __inner_type_impl(__inner_type_impl<_Allocs...>&& __other) noexcept
150  : _M_inner(std::move(__other._M_inner)) { }
151 
152  template<typename... _Args>
153  explicit
154  __inner_type_impl(_Args&&... __args) noexcept
155  : _M_inner(std::forward<_Args>(__args)...) { }
156 
157  __type&
158  _M_get(void*) noexcept { return _M_inner; }
159 
160  const __type&
161  _M_get(const void*) const noexcept { return _M_inner; }
162 
163  tuple<const _InnerHead&, const _InnerTail&...>
164  _M_tie() const noexcept
165  { return _M_inner._M_tie(); }
166 
167  bool
168  operator==(const __inner_type_impl& __other) const noexcept
169  { return _M_inner == __other._M_inner; }
170 
171  private:
172  template<typename...> friend struct __inner_type_impl;
173  template<typename, typename...> friend class scoped_allocator_adaptor;
174 
175  __type _M_inner;
176  };
177 
178  /// @endcond
179 
180  /// An adaptor to recursively pass an allocator to the objects it constructs
181  template<typename _OuterAlloc, typename... _InnerAllocs>
182  class scoped_allocator_adaptor
183  : public _OuterAlloc
184  {
185  typedef allocator_traits<_OuterAlloc> __traits;
186 
187  typedef __inner_type_impl<_OuterAlloc, _InnerAllocs...> __inner_type;
188  __inner_type _M_inner;
189 
190  template<typename _Outer, typename... _Inner>
191  friend class scoped_allocator_adaptor;
192 
193  template<typename...>
194  friend struct __inner_type_impl;
195 
196  tuple<const _OuterAlloc&, const _InnerAllocs&...>
197  _M_tie() const noexcept
198  { return std::tuple_cat(std::tie(outer_allocator()), _M_inner._M_tie()); }
199 
200  template<typename _Alloc>
201  using __outermost_alloc_traits
202  = allocator_traits<typename __outermost_type<_Alloc>::type>;
203 
204 #if ! __glibcxx_make_obj_using_allocator
205  template<typename _Tp, typename... _Args>
206  void
207  _M_construct(__uses_alloc0, _Tp* __p, _Args&&... __args)
208  {
209  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
210  _O_traits::construct(__outermost(*this), __p,
211  std::forward<_Args>(__args)...);
212  }
213 
214  typedef __uses_alloc1<typename __inner_type::__type> __uses_alloc1_;
215  typedef __uses_alloc2<typename __inner_type::__type> __uses_alloc2_;
216 
217  template<typename _Tp, typename... _Args>
218  void
219  _M_construct(__uses_alloc1_, _Tp* __p, _Args&&... __args)
220  {
221  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
222  _O_traits::construct(__outermost(*this), __p,
223  allocator_arg, inner_allocator(),
224  std::forward<_Args>(__args)...);
225  }
226 
227  template<typename _Tp, typename... _Args>
228  void
229  _M_construct(__uses_alloc2_, _Tp* __p, _Args&&... __args)
230  {
231  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
232  _O_traits::construct(__outermost(*this), __p,
233  std::forward<_Args>(__args)...,
234  inner_allocator());
235  }
236 #endif // ! make_obj_using_allocator
237 
238  template<typename _Alloc>
239  static _Alloc
240  _S_select_on_copy(const _Alloc& __a)
241  {
242  typedef allocator_traits<_Alloc> __a_traits;
243  return __a_traits::select_on_container_copy_construction(__a);
244  }
245 
246  template<std::size_t... _Indices>
247  scoped_allocator_adaptor(tuple<const _OuterAlloc&,
248  const _InnerAllocs&...> __refs,
249  _Index_tuple<_Indices...>)
250  : _OuterAlloc(_S_select_on_copy(std::get<0>(__refs))),
251  _M_inner(_S_select_on_copy(std::get<_Indices+1>(__refs))...)
252  { }
253 
254  // Used to constrain constructors to disallow invalid conversions.
255  template<typename _Alloc>
256  using _Constructible = typename enable_if<
257  is_constructible<_OuterAlloc, _Alloc>::value
258  >::type;
259 
260  // _GLIBCXX_RESOLVE_LIB_DEFECTS
261  // 2975. Missing case for pair construction in scoped [...] allocators
262  template<typename _Tp>
263  struct __not_pair { using type = void; };
264 
265  template<typename _Tp, typename _Up>
266  struct __not_pair<pair<_Tp, _Up>> { };
267 
268  public:
269  typedef _OuterAlloc outer_allocator_type;
270  typedef typename __inner_type::__type inner_allocator_type;
271 
272  typedef typename __traits::value_type value_type;
273  typedef typename __traits::size_type size_type;
274  typedef typename __traits::difference_type difference_type;
275  typedef typename __traits::pointer pointer;
276  typedef typename __traits::const_pointer const_pointer;
277  typedef typename __traits::void_pointer void_pointer;
278  typedef typename __traits::const_void_pointer const_void_pointer;
279 
280  typedef typename __or_<
281  typename __traits::propagate_on_container_copy_assignment,
282  typename allocator_traits<_InnerAllocs>::
283  propagate_on_container_copy_assignment...>::type
284  propagate_on_container_copy_assignment;
285 
286  typedef typename __or_<
287  typename __traits::propagate_on_container_move_assignment,
288  typename allocator_traits<_InnerAllocs>::
289  propagate_on_container_move_assignment...>::type
290  propagate_on_container_move_assignment;
291 
292  typedef typename __or_<
293  typename __traits::propagate_on_container_swap,
294  typename allocator_traits<_InnerAllocs>::
295  propagate_on_container_swap...>::type
296  propagate_on_container_swap;
297 
298  typedef typename __and_<
299  typename __traits::is_always_equal,
300  typename allocator_traits<_InnerAllocs>::is_always_equal...>::type
301  is_always_equal;
302 
303  template <class _Tp>
304  struct rebind
305  {
306  typedef scoped_allocator_adaptor<
307  typename __traits::template rebind_alloc<_Tp>,
308  _InnerAllocs...> other;
309  };
310 
311  scoped_allocator_adaptor() : _OuterAlloc(), _M_inner() { }
312 
313  template<typename _Outer2, typename = _Constructible<_Outer2>>
314  scoped_allocator_adaptor(_Outer2&& __outer,
315  const _InnerAllocs&... __inner) noexcept
316  : _OuterAlloc(std::forward<_Outer2>(__outer)),
317  _M_inner(__inner...)
318  { }
319 
320  scoped_allocator_adaptor(const scoped_allocator_adaptor& __other) noexcept
321  : _OuterAlloc(__other.outer_allocator()),
322  _M_inner(__other._M_inner)
323  { }
324 
325  scoped_allocator_adaptor(scoped_allocator_adaptor&& __other) noexcept
326  : _OuterAlloc(std::move(__other.outer_allocator())),
327  _M_inner(std::move(__other._M_inner))
328  { }
329 
330  template<typename _Outer2, typename = _Constructible<const _Outer2&>>
331  scoped_allocator_adaptor(
332  const scoped_allocator_adaptor<_Outer2, _InnerAllocs...>& __other
333  ) noexcept
334  : _OuterAlloc(__other.outer_allocator()),
335  _M_inner(__other._M_inner)
336  { }
337 
338  template<typename _Outer2, typename = _Constructible<_Outer2>>
339  scoped_allocator_adaptor(
340  scoped_allocator_adaptor<_Outer2, _InnerAllocs...>&& __other) noexcept
341  : _OuterAlloc(std::move(__other.outer_allocator())),
342  _M_inner(std::move(__other._M_inner))
343  { }
344 
345  scoped_allocator_adaptor&
346  operator=(const scoped_allocator_adaptor&) = default;
347 
348  scoped_allocator_adaptor&
349  operator=(scoped_allocator_adaptor&&) = default;
350 
351  inner_allocator_type&
352  inner_allocator() noexcept
353  { return _M_inner._M_get(this); }
354 
355  const inner_allocator_type&
356  inner_allocator() const noexcept
357  { return _M_inner._M_get(this); }
358 
359  outer_allocator_type&
360  outer_allocator() noexcept
361  { return static_cast<_OuterAlloc&>(*this); }
362 
363  const outer_allocator_type&
364  outer_allocator() const noexcept
365  { return static_cast<const _OuterAlloc&>(*this); }
366 
367  _GLIBCXX_NODISCARD pointer
368  allocate(size_type __n)
369  { return __traits::allocate(outer_allocator(), __n); }
370 
371  _GLIBCXX_NODISCARD pointer
372  allocate(size_type __n, const_void_pointer __hint)
373  { return __traits::allocate(outer_allocator(), __n, __hint); }
374 
375  void deallocate(pointer __p, size_type __n) noexcept
376  { return __traits::deallocate(outer_allocator(), __p, __n); }
377 
378  size_type max_size() const
379  { return __traits::max_size(outer_allocator()); }
380 
381 #if ! __glibcxx_make_obj_using_allocator
382  template<typename _Tp, typename... _Args>
383  typename __not_pair<_Tp>::type
384  construct(_Tp* __p, _Args&&... __args)
385  {
386  auto& __inner = inner_allocator();
387  auto __use_tag
388  = std::__use_alloc<_Tp, inner_allocator_type, _Args...>(__inner);
389  _M_construct(__use_tag, __p, std::forward<_Args>(__args)...);
390  }
391 
392  template<typename _T1, typename _T2, typename... _Args1,
393  typename... _Args2>
394  void
395  construct(pair<_T1, _T2>* __p, piecewise_construct_t,
396  tuple<_Args1...> __x, tuple<_Args2...> __y)
397  {
398  // _GLIBCXX_RESOLVE_LIB_DEFECTS
399  // 2203. wrong argument types for piecewise construction
400  auto& __inner = inner_allocator();
401  auto __x_use_tag
402  = std::__use_alloc<_T1, inner_allocator_type, _Args1...>(__inner);
403  auto __y_use_tag
404  = std::__use_alloc<_T2, inner_allocator_type, _Args2...>(__inner);
405  typename _Build_index_tuple<sizeof...(_Args1)>::__type __x_indices;
406  typename _Build_index_tuple<sizeof...(_Args2)>::__type __y_indices;
407  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
408  _O_traits::construct(__outermost(*this), __p, piecewise_construct,
409  _M_construct_p(__x_use_tag, __x_indices, __x),
410  _M_construct_p(__y_use_tag, __y_indices, __y));
411  }
412 
413  template<typename _T1, typename _T2>
414  void
415  construct(pair<_T1, _T2>* __p)
416  { construct(__p, piecewise_construct, tuple<>(), tuple<>()); }
417 
418  template<typename _T1, typename _T2, typename _Up, typename _Vp>
419  void
420  construct(pair<_T1, _T2>* __p, _Up&& __u, _Vp&& __v)
421  {
422  construct(__p, piecewise_construct,
423  std::forward_as_tuple(std::forward<_Up>(__u)),
424  std::forward_as_tuple(std::forward<_Vp>(__v)));
425  }
426 
427  template<typename _T1, typename _T2, typename _Up, typename _Vp>
428  void
429  construct(pair<_T1, _T2>* __p, const pair<_Up, _Vp>& __x)
430  {
431  construct(__p, piecewise_construct,
432  std::forward_as_tuple(__x.first),
433  std::forward_as_tuple(__x.second));
434  }
435 
436  template<typename _T1, typename _T2, typename _Up, typename _Vp>
437  void
438  construct(pair<_T1, _T2>* __p, pair<_Up, _Vp>&& __x)
439  {
440  construct(__p, piecewise_construct,
441  std::forward_as_tuple(std::forward<_Up>(__x.first)),
442  std::forward_as_tuple(std::forward<_Vp>(__x.second)));
443  }
444 #else // make_obj_using_allocator
445  template<typename _Tp, typename... _Args>
446  __attribute__((__nonnull__))
447  void
448  construct(_Tp* __p, _Args&&... __args)
449  {
450  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
451  std::apply([__p, this](auto&&... __newargs) {
452  _O_traits::construct(__outermost(*this), __p,
453  std::forward<decltype(__newargs)>(__newargs)...);
454  },
455  uses_allocator_construction_args<_Tp>(inner_allocator(),
456  std::forward<_Args>(__args)...));
457  }
458 #endif
459 
460  template<typename _Tp>
461  void destroy(_Tp* __p)
462  {
463  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
464  _O_traits::destroy(__outermost(*this), __p);
465  }
466 
467  scoped_allocator_adaptor
468  select_on_container_copy_construction() const
469  {
470  typedef typename _Build_index_tuple<sizeof...(_InnerAllocs)>::__type
471  _Indices;
472  return scoped_allocator_adaptor(_M_tie(), _Indices());
473  }
474 
475  template <typename _OutA1, typename _OutA2, typename... _InA>
476  friend bool
477  operator==(const scoped_allocator_adaptor<_OutA1, _InA...>& __a,
478  const scoped_allocator_adaptor<_OutA2, _InA...>& __b) noexcept;
479 
480  private:
481 #if ! __glibcxx_make_obj_using_allocator
482  template<typename _Ind, typename... _Args>
483  tuple<_Args&&...>
484  _M_construct_p(__uses_alloc0, _Ind, tuple<_Args...>& __t)
485  { return std::move(__t); }
486 
487  template<size_t... _Ind, typename... _Args>
488  tuple<allocator_arg_t, inner_allocator_type&, _Args&&...>
489  _M_construct_p(__uses_alloc1_, _Index_tuple<_Ind...>,
490  tuple<_Args...>& __t)
491  {
492  return { allocator_arg, inner_allocator(),
493  std::get<_Ind>(std::move(__t))...
494  };
495  }
496 
497  template<size_t... _Ind, typename... _Args>
498  tuple<_Args&&..., inner_allocator_type&>
499  _M_construct_p(__uses_alloc2_, _Index_tuple<_Ind...>,
500  tuple<_Args...>& __t)
501  {
502  return { std::get<_Ind>(std::move(__t))..., inner_allocator() };
503  }
504 #endif // ! make_obj_using_allocator
505  };
506 
507  /// @related std::scoped_allocator_adaptor
508  template <typename _OutA1, typename _OutA2, typename... _InA>
509  inline bool
510  operator==(const scoped_allocator_adaptor<_OutA1, _InA...>& __a,
511  const scoped_allocator_adaptor<_OutA2, _InA...>& __b) noexcept
512  {
513  return __a.outer_allocator() == __b.outer_allocator()
514  && __a._M_inner == __b._M_inner;
515  }
516 
517 #if __cpp_impl_three_way_comparison < 201907L
518  /// @related std::scoped_allocator_adaptor
519  template <typename _OutA1, typename _OutA2, typename... _InA>
520  inline bool
521  operator!=(const scoped_allocator_adaptor<_OutA1, _InA...>& __a,
522  const scoped_allocator_adaptor<_OutA2, _InA...>& __b) noexcept
523  { return !(__a == __b); }
524 #endif
525 
526  /// @}
527 
528 _GLIBCXX_END_NAMESPACE_VERSION
529 } // namespace
530 
531 #endif // C++11
532 
533 #endif // _SCOPED_ALLOCATOR