Am Do., 22. Mai 2025 um 11:41 Uhr schrieb Tomasz Kamiński < tkami...@redhat.com>:
> From: Jonathan Wakely <jwak...@redhat.com> > > libstdc++-v3/ChangeLog: > > * include/bits/allocated_ptr.h (_Scoped_allocation): New class > template. > > Co-Authored-By: Tomasz Kamiński <tkami...@redhat.com> > Signed-off-by: Tomasz Kamiński <tkami...@redhat.com> > --- > Tested on x86_64-linux. OK for trunk? > > libstdc++-v3/include/bits/allocated_ptr.h | 96 +++++++++++++++++++++++ > 1 file changed, 96 insertions(+) > > diff --git a/libstdc++-v3/include/bits/allocated_ptr.h > b/libstdc++-v3/include/bits/allocated_ptr.h > index 0b2b6fe5820..aa5355f0e2f 100644 > --- a/libstdc++-v3/include/bits/allocated_ptr.h > +++ b/libstdc++-v3/include/bits/allocated_ptr.h > @@ -36,6 +36,7 @@ > # include <type_traits> > # include <bits/ptr_traits.h> > # include <bits/alloc_traits.h> > +# include <bits/utility.h> > > namespace std _GLIBCXX_VISIBILITY(default) > { > @@ -136,6 +137,101 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return { std::__allocate_guarded(__a) }; > } > > + // An RAII type that acquires memory from an allocator. > + // N.B. 'scoped' here in in the RAII sense, not the scoped allocator > model, > + // so this has nothing to do with `std::scoped_allocator_adaptor`. > + // This class can be used to simplify the common pattern: > + // > + // auto ptr = alloc.allocate(1); > + // try { > + // std::construct_at(std::to_address(ptr), args); > + // m_ptr = ptr; > + // } catch (...) { > + // alloc.deallocate(ptr, 1); > + // throw; > + // } > + // > + // Instead you can do: > + // > + // _Scoped_allocation sa(alloc); > + // m_ptr = std::construct_at(sa.get(), args); > + // (void) sa.release(); > + // > + // Or even simpler: > + // > + // _Scoped_allocation sa(alloc, std::in_place, args); > + // m_ptr = sa.release(); > + // > + template<typename _Alloc> > + struct _Scoped_allocation > + { > + using value_type = typename allocator_traits<_Alloc>::value_type; > + using pointer = typename allocator_traits<_Alloc>::pointer; > + > + // Use `a` to allocate memory for `n` objects. > + constexpr explicit > + _Scoped_allocation(const _Alloc& __a, size_t __n = 1) > + : _M_a(__a), _M_n(__n), _M_p(_M_a.allocate(__n)) > + { } > + > +#if __glibcxx_optional >= 201606L > + // Allocate memory for a single object and if that succeeds, > + // construct an object using args. > + // > + // Does not do uses-allocator construction; don't use if you need > that. > + // > + // CAUTION: the destructor will *not* destroy this object, it will > only > + // free the memory. That means the following pattern is unsafe: > + // > + // _Scoped_allocation sa(alloc, in_place, args); > + // potentially_throwing_operations(); > + // return sa.release(); > + // > + // If the middle operation throws, the object will not be destroyed. > + template<typename... _Args> > + constexpr explicit > + _Scoped_allocation(const _Alloc& __a, in_place_t, _Args&&... > __args) > + : _Scoped_allocation(__a, 1) > + { > + // The target constructor has completed, so if the next line > throws, > + // the destructor will deallocate the memory. > + allocator_traits<_Alloc>::construct(_M_a, get(), > + > std::forward<_Args>(__args)...); > + } > +#endif > + > + _GLIBCXX20_CONSTEXPR > + ~_Scoped_allocation() > + { > + if (_M_p) [[__unlikely__]] > + _M_a.deallocate(_M_p, _M_n); > Why is the situation *unlikely* that _M_p has a non-nullptr content? Shouldn't that actually be likely? - Daniel > + } > + > + _Scoped_allocation(_Scoped_allocation&&) = delete; > + > + constexpr _Alloc > + get_allocator() const noexcept { return _M_a; } > + > + constexpr value_type* > + get() const noexcept > + { return std::__to_address(_M_p); } > + > + [[__nodiscard__]] > + constexpr pointer > + release() noexcept { return std::__exchange(_M_p, nullptr); } > + > + private: > + [[__no_unique_address__]] _Alloc _M_a; > + size_t _M_n; > + pointer _M_p; > + }; > + > +#if __glibcxx_optional >= 201606L && __cpp_deduction_guides >= 201606L > + template<typename _Alloc, typename... _Args> > + _Scoped_allocation(_Alloc, in_place_t, _Args...) > + -> _Scoped_allocation<_Alloc>; > +#endif > + > /// @endcond > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > -- > 2.49.0 > >