We could also use this in all containers (and node handles) and in the control block created by std::allocate_shared.
To use it for containers we will need to decide whether to change the non-standard std::_Destroy(FwdIter, FwdIter, Allocator&) to use this, or whether to add a new _Destroy_static function and make the containers use that instead. For this RFC patch, I've only used it in a few places in std::vector where a single object is created/destroyed directly, so not when destroying multiple elements using std::_Destroy. Tested x86_64-linux. -- >8 -- Add a version of allocator_traits::destroy that can be used when the static type is known, so that we can make an explicit destructor call that avoids virtual lookup. This is only possible when the allocator doesn't provide a destroy member, so that we know we're going to invoke the destructor directly. In containers like std::vector we know that we're never destroying elements through a pointer to base, because the dynamic type is known to be the same as the static type. libstdc++-v3/ChangeLog: PR libstdc++/110057 * include/bits/stl_vector.h: Use _Destroy_static_type instead of destroy. * include/bits/vector.tcc: Likewise. * include/ext/alloc_traits.h (__alloc_traits::_Destroy_static_type): New function template. --- libstdc++-v3/include/bits/stl_vector.h | 5 ++-- libstdc++-v3/include/bits/vector.tcc | 3 ++- libstdc++-v3/include/ext/alloc_traits.h | 32 ++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h index e284536ad31..f7451fa9e18 100644 --- a/libstdc++-v3/include/bits/stl_vector.h +++ b/libstdc++-v3/include/bits/stl_vector.h @@ -1325,7 +1325,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER { __glibcxx_requires_nonempty(); --this->_M_impl._M_finish; - _Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish); + _Alloc_traits::_Destroy_static_type(this->_M_impl, + this->_M_impl._M_finish); _GLIBCXX_ASAN_ANNOTATE_SHRINK(1); } @@ -1870,7 +1871,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _GLIBCXX20_CONSTEXPR ~_Temporary_value() - { _Alloc_traits::destroy(_M_this->_M_impl, _M_ptr()); } + { _Alloc_traits::_Destroy_static_type(_M_this->_M_impl, _M_ptr()); } _GLIBCXX20_CONSTEXPR value_type& _M_val() noexcept { return _M_storage._M_val; } diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc index a99a5b56b77..660a8b4e414 100644 --- a/libstdc++-v3/include/bits/vector.tcc +++ b/libstdc++-v3/include/bits/vector.tcc @@ -184,7 +184,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER if (__position + 1 != end()) _GLIBCXX_MOVE3(__position + 1, end(), __position); --this->_M_impl._M_finish; - _Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish); + _Alloc_traits::_Destroy_static_type(this->_M_impl, + this->_M_impl._M_finish); _GLIBCXX_ASAN_ANNOTATE_SHRINK(1); return __position; } diff --git a/libstdc++-v3/include/ext/alloc_traits.h b/libstdc++-v3/include/ext/alloc_traits.h index d2560531bac..17caaee2715 100644 --- a/libstdc++-v3/include/ext/alloc_traits.h +++ b/libstdc++-v3/include/ext/alloc_traits.h @@ -33,7 +33,7 @@ #pragma GCC system_header #endif -# include <bits/alloc_traits.h> +#include <bits/alloc_traits.h> namespace __gnu_cxx _GLIBCXX_VISIBILITY(default) { @@ -95,6 +95,30 @@ template<typename _Alloc, typename = typename _Alloc::value_type> noexcept(noexcept(_Base_type::destroy(__a, std::__to_address(__p)))) { _Base_type::destroy(__a, std::__to_address(__p)); } + // Equivalent to `destroy` except that when `a.destroy(p)` is not valid, + // the destructor will be called using a qualified name, so that no + // dynamic dispatch to a virtual destructor is done. This can be used + // in e.g. std::vector where we know that the elements do not have a + // dynamic type that is different from the static type. + template<typename _Ptr> + [[__gnu__::__always_inline__]] + static _GLIBCXX20_CONSTEXPR void + _Destroy_static_type(_Alloc& __a, _Ptr __p) + { +#if __cpp_concepts + auto __ptr = std::__to_address(__p); + if constexpr (requires { __a.destroy(__ptr); }) + __a.destroy(__ptr); + else + { + using _Tp = std::remove_pointer_t<decltype(__ptr)>; + __ptr->_Tp::~_Tp(); + } +#else + destroy(__a, __p); +#endif + } + [[__gnu__::__always_inline__]] static constexpr _Alloc _S_select_on_copy(const _Alloc& __a) { return _Base_type::select_on_container_copy_construction(__a); } @@ -178,6 +202,12 @@ template<typename _Alloc, typename = typename _Alloc::value_type> template<typename _Tp> struct rebind { typedef typename _Alloc::template rebind<_Tp>::other other; }; + + template<typename _Ptr> + __attribute__((__always_inline__)) + static void + _Destroy_static_type(_Alloc& __a, _Ptr __p) + { __a.destroy(__p); } #endif // C++11 }; -- 2.46.1