Hello, this patch passed bootstrap+regtest on powerpc64le-unknown-linux-gnu.
The main idea is manually performing loop fusion when we see 2 consecutive loops where the first moves data from A to B, and the second destroys the same elements in A.
This is beneficial because there is one loop fewer (as usual with loop fusion), but also because the move constructor and the destructor can combine relatively well for several types. I performed some simple tests on std::vector<std::string>, and for a loop that just emplace_back many empty strings, I see a performance gain close to 20%. With not-small strings, I am still seeing a noticable gain (maybe 10%? The noise makes it hard to be precise).
I had to add a special case for trivial types, using memmove, to avoid perf regressions, since relocation takes precedence over the old path that is specialized to call memmove.
_GLIBCXX_ASAN_ANNOTATE_REINIT: I am not familiar with those annotations. It was convenient in one function to move this annotation after _Destroy, to reduce code duplication. For consistency, I did the same in the whole file. As far as I understand, the macro makes it ok to access memory between _M_finish and _M_end_of_storage, and at the end of the block marks again the region after the new _M_finish as protected. Since _Destroy should stop at _M_finish, moving the macro looks safe. But maybe the position of the macro was chosen to reduce needless checking in ASAN?
It might be possible to introduce some helpers that do relocate if noexcept and copy otherwise, but it is much less convenient than for move_if_noexcept, because they don't want to execute the destructors at the same point, so we might also want a destroy_if_no_relocate to go with it...
The exact form of the relocate functions is whatever I had when things started working, it is probably not that important as long as we don't document them. I had a _n version taking a size, but I ended up not using it, so I removed it.
The change is limited to C++17+, because I felt like using if constexpr. Actually, I think g++ accepts if constexpr in C++11 with a warning (ok in a system header), I don't remember if I ended up using any other recent features...
Possible future stuff:* use relocation in more places: there should be 1 or 2 places left in vector, deque may also be a good candidate, I didn't look elsewhere.
* specialize relocation for some types (maybe deque?) where it can be noexcept, possibly even trivial, whereas the move constructor cannot. If we do that, we may want to specialize for pair/tuple/array as well, in case one of the members is specialized.
2018-09-01 Marc Glisse <marc.gli...@inria.fr> PR libstdc++/87106 * include/bits/alloc_traits.h (_S_construct, _S_destroy, construct, destroy): Add noexcept specification. * include/bits/allocator.h (construct, destroy): Likewise. * include/ext/alloc_traits.h (construct, destroy): Likewise. * include/ext/malloc_allocator.h (construct, destroy): Likewise. * include/ext/new_allocator.h (construct, destroy): Likewise. * include/bits/stl_uninitialized.h (__relocate, __relocate_a, __relocate_a_1): New functions. (__is_trivially_relocatable): New class. * include/bits/stl_vector.h (__use_relocate): New static member. * include/bits/vector.tcc (reserve, _M_realloc_insert, _M_default_append): Use __relocate_a. (reserve, _M_assign_aux, _M_realloc_insert, _M_fill_insert, _M_default_append, _M_range_insert): Move _GLIBCXX_ASAN_ANNOTATE_REINIT after _Destroy. -- Marc Glisse
Index: include/bits/alloc_traits.h =================================================================== --- include/bits/alloc_traits.h (revision 264027) +++ include/bits/alloc_traits.h (working copy) @@ -233,38 +233,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using type = decltype(__test<_Alloc>(0)); }; template<typename _Tp, typename... _Args> using __has_construct = typename __construct_helper<_Tp, _Args...>::type; template<typename _Tp, typename... _Args> static _Require<__has_construct<_Tp, _Args...>> _S_construct(_Alloc& __a, _Tp* __p, _Args&&... __args) + noexcept(noexcept(__a.construct(__p, std::forward<_Args>(__args)...))) { __a.construct(__p, std::forward<_Args>(__args)...); } template<typename _Tp, typename... _Args> static _Require<__and_<__not_<__has_construct<_Tp, _Args...>>, is_constructible<_Tp, _Args...>>> _S_construct(_Alloc&, _Tp* __p, _Args&&... __args) + noexcept(noexcept(::new((void*)__p) + _Tp(std::forward<_Args>(__args)...))) { ::new((void*)__p) _Tp(std::forward<_Args>(__args)...); } template<typename _Alloc2, typename _Tp> static auto _S_destroy(_Alloc2& __a, _Tp* __p, int) + noexcept(noexcept(__a.destroy(__p))) -> decltype(__a.destroy(__p)) { __a.destroy(__p); } template<typename _Alloc2, typename _Tp> static void _S_destroy(_Alloc2&, _Tp* __p, ...) + noexcept(noexcept(__p->~_Tp())) { __p->~_Tp(); } template<typename _Alloc2> static auto _S_max_size(_Alloc2& __a, int) -> decltype(__a.max_size()) { return __a.max_size(); } template<typename _Alloc2> static size_type @@ -333,33 +338,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @param __p Pointer to memory of suitable size and alignment for Tp * @param __args Constructor arguments. * * Calls <tt> __a.construct(__p, std::forward<Args>(__args)...) </tt> * if that expression is well-formed, otherwise uses placement-new * to construct an object of type @a _Tp at location @a __p from the * arguments @a __args... */ template<typename _Tp, typename... _Args> static auto construct(_Alloc& __a, _Tp* __p, _Args&&... __args) + noexcept(noexcept(_S_construct(__a, __p, + std::forward<_Args>(__args)...))) -> decltype(_S_construct(__a, __p, std::forward<_Args>(__args)...)) { _S_construct(__a, __p, std::forward<_Args>(__args)...); } /** * @brief Destroy an object of type @a _Tp * @param __a An allocator. * @param __p Pointer to the object to destroy * * Calls @c __a.destroy(__p) if that expression is well-formed, * otherwise calls @c __p->~_Tp() */ template<typename _Tp> static void destroy(_Alloc& __a, _Tp* __p) + noexcept(noexcept(_S_destroy(__a, __p, 0))) { _S_destroy(__a, __p, 0); } /** * @brief The maximum supported allocation size * @param __a An allocator. * @return @c __a.max_size() or @c numeric_limits<size_type>::max() * * Returns @c __a.max_size() if that expression is well-formed, * otherwise returns @c numeric_limits<size_type>::max() */ @@ -465,32 +473,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @brief Construct an object of type @a _Up * @param __a An allocator. * @param __p Pointer to memory of suitable size and alignment for Tp * @param __args Constructor arguments. * * Calls <tt> __a.construct(__p, std::forward<Args>(__args)...) </tt> */ template<typename _Up, typename... _Args> static void construct(allocator_type& __a, _Up* __p, _Args&&... __args) + noexcept(noexcept(__a.construct(__p, std::forward<_Args>(__args)...))) { __a.construct(__p, std::forward<_Args>(__args)...); } /** * @brief Destroy an object of type @a _Up * @param __a An allocator. * @param __p Pointer to the object to destroy * * Calls @c __a.destroy(__p). */ template<typename _Up> static void destroy(allocator_type& __a, _Up* __p) + noexcept(noexcept(__a.destroy(__p))) { __a.destroy(__p); } /** * @brief The maximum supported allocation size * @param __a An allocator. * @return @c __a.max_size() */ static size_type max_size(const allocator_type& __a) noexcept { return __a.max_size(); } Index: include/bits/allocator.h =================================================================== --- include/bits/allocator.h (revision 264027) +++ include/bits/allocator.h (working copy) @@ -81,25 +81,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cplusplus >= 201103L // _GLIBCXX_RESOLVE_LIB_DEFECTS // 2103. std::allocator propagate_on_container_move_assignment typedef true_type propagate_on_container_move_assignment; typedef true_type is_always_equal; template<typename _Up, typename... _Args> void construct(_Up* __p, _Args&&... __args) + noexcept(noexcept(::new((void *)__p) + _Up(std::forward<_Args>(__args)...))) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } template<typename _Up> void - destroy(_Up* __p) { __p->~_Up(); } + destroy(_Up* __p) + noexcept(noexcept(__p->~_Up())) + { __p->~_Up(); } #endif }; /** * @brief The @a standard allocator, as per [20.4]. * * See https://gcc.gnu.org/onlinedocs/libstdc++/manual/memory.html#std.util.memory.allocator * for further details. * * @tparam _Tp Type of allocated object. Index: include/bits/stl_uninitialized.h =================================================================== --- include/bits/stl_uninitialized.h (revision 264027) +++ include/bits/stl_uninitialized.h (working copy) @@ -872,14 +872,85 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION uninitialized_move_n(_InputIterator __first, _Size __count, _ForwardIterator __result) { auto __res = std::__uninitialized_copy_n_pair (_GLIBCXX_MAKE_MOVE_ITERATOR(__first), __count, __result); return {__res.first.base(), __res.second}; } #endif +#if __cplusplus >= 201703L + template<typename _Tp, typename _Up> + inline void + __relocate(_Tp* __dest, _Up* __orig) + noexcept(is_nothrow_constructible<_Tp, _Up&&>::value) + { + ::new((void*)__dest) _Tp(std::move(*__orig)); + __orig->~_Up(); + } + + template<typename _Tp, typename _Up, typename _Allocator> + inline void + __relocate_a(_Tp* __dest, _Up* __orig, _Allocator& __alloc) + // people who oppose noexcept(auto) clearly never write C++ themselves + noexcept(noexcept(__gnu_cxx::__alloc_traits<_Allocator>::construct(__alloc, + __dest, std::move(*__orig))) + && noexcept(__gnu_cxx::__alloc_traits<_Allocator>::destroy( + __alloc, std::__addressof(*__orig)))) + { + typedef __gnu_cxx::__alloc_traits<_Allocator> __traits; + __traits::construct(__alloc, __dest, std::move(*__orig)); + __traits::destroy(__alloc, std::__addressof(*__orig)); + } + + template<typename _Tp> + struct __is_trivially_relocatable + : is_trivially_move_constructible<_Tp> { }; + + template <typename _Tp, typename _Up> + inline std::enable_if_t<std::__is_trivially_relocatable<_Tp>::value, _Tp*> + __relocate_a_1(_Tp* __first, _Tp* __last, + _Tp* __result, allocator<_Up>& __alloc) + { + ptrdiff_t __count = __last - __first; + __builtin_memmove(__result, __first, __count * sizeof(_Tp)); + return __result + __count; + } + + template <typename _InputIterator, typename _ForwardIterator, + typename _Allocator> + inline _ForwardIterator + __relocate_a_1(_InputIterator __first, _InputIterator __last, + _ForwardIterator __result, _Allocator& __alloc) + { + typedef typename iterator_traits<_InputIterator>::value_type + _ValueType; + typedef typename iterator_traits<_ForwardIterator>::value_type + _ValueType2; + static_assert(std::is_same_v<_ValueType, _ValueType2>); + static_assert(noexcept(std::__relocate_a(std::addressof(*__result), + std::addressof(*__first), + __alloc))); + _ForwardIterator __cur = __result; + for (; __first != __last; ++__first, (void)++__cur) + std::__relocate_a(std::__addressof(*__cur), + std::__addressof(*__first), __alloc); + return __cur; + } + + template <typename _InputIterator, typename _ForwardIterator, + typename _Allocator> + inline _ForwardIterator + __relocate_a(_InputIterator __first, _InputIterator __last, + _ForwardIterator __result, _Allocator& __alloc) + { + return __relocate_a_1(std::__niter_base(__first), + std::__niter_base(__last), + std::__niter_base(__result), __alloc); + } +#endif + _GLIBCXX_END_NAMESPACE_VERSION } // namespace #endif /* _STL_UNINITIALIZED_H */ Index: include/bits/stl_vector.h =================================================================== --- include/bits/stl_vector.h (revision 264027) +++ include/bits/stl_vector.h (working copy) @@ -398,20 +398,26 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER "std::vector must have a non-const, non-volatile value_type"); # ifdef __STRICT_ANSI__ static_assert(is_same<typename _Alloc::value_type, _Tp>::value, "std::vector must have the same value_type as its allocator"); # endif #endif typedef _Vector_base<_Tp, _Alloc> _Base; typedef typename _Base::_Tp_alloc_type _Tp_alloc_type; typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Alloc_traits; +#if __cplusplus >= 201703L + static constexpr bool __use_relocate = + noexcept(std::__relocate_a(std::declval<_Tp*>(), + std::declval<_Tp*>(), + std::declval<_Tp_alloc_type&>())); +#endif public: typedef _Tp value_type; typedef typename _Base::pointer pointer; typedef typename _Alloc_traits::const_pointer const_pointer; typedef typename _Alloc_traits::reference reference; typedef typename _Alloc_traits::const_reference const_reference; typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator; typedef __gnu_cxx::__normal_iterator<const_pointer, vector> const_iterator; Index: include/bits/vector.tcc =================================================================== --- include/bits/vector.tcc (revision 264027) +++ include/bits/vector.tcc (working copy) @@ -64,26 +64,39 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER template<typename _Tp, typename _Alloc> void vector<_Tp, _Alloc>:: reserve(size_type __n) { if (__n > this->max_size()) __throw_length_error(__N("vector::reserve")); if (this->capacity() < __n) { const size_type __old_size = size(); - pointer __tmp = _M_allocate_and_copy(__n, - _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_start), - _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_finish)); + pointer __tmp; +#if __cplusplus >= 201703L + if constexpr (__use_relocate) + { + __tmp = this->_M_allocate(__n); + std::__relocate_a(this->_M_impl._M_start, + this->_M_impl._M_finish, + __tmp, _M_get_Tp_allocator()); + } + else +#endif + { + __tmp = _M_allocate_and_copy(__n, + _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_start), + _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_finish)); + std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, + _M_get_Tp_allocator()); + } _GLIBCXX_ASAN_ANNOTATE_REINIT; - std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, - _M_get_Tp_allocator()); _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __tmp; this->_M_impl._M_finish = __tmp + __old_size; this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n; } } #if __cplusplus >= 201103L @@ -288,23 +301,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER vector<_Tp, _Alloc>:: _M_assign_aux(_ForwardIterator __first, _ForwardIterator __last, std::forward_iterator_tag) { const size_type __len = std::distance(__first, __last); if (__len > capacity()) { _S_check_init_len(__len, _M_get_Tp_allocator()); pointer __tmp(_M_allocate_and_copy(__len, __first, __last)); - _GLIBCXX_ASAN_ANNOTATE_REINIT; std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); + _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __tmp; this->_M_impl._M_finish = this->_M_impl._M_start + __len; this->_M_impl._M_end_of_storage = this->_M_impl._M_finish; } else if (size() >= __len) _M_erase_at_end(std::copy(__first, __last, this->_M_impl._M_start)); else @@ -436,44 +449,66 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER // [res.on.arguments]). _Alloc_traits::construct(this->_M_impl, __new_start + __elems_before, #if __cplusplus >= 201103L std::forward<_Args>(__args)...); #else __x); #endif __new_finish = pointer(); - __new_finish - = std::__uninitialized_move_if_noexcept_a - (__old_start, __position.base(), - __new_start, _M_get_Tp_allocator()); - - ++__new_finish; - - __new_finish - = std::__uninitialized_move_if_noexcept_a - (__position.base(), __old_finish, - __new_finish, _M_get_Tp_allocator()); +#if __cplusplus >= 201703L + if constexpr (__use_relocate) + { + __new_finish + = std::__relocate_a + (__old_start, __position.base(), + __new_start, _M_get_Tp_allocator()); + + ++__new_finish; + + __new_finish + = std::__relocate_a + (__position.base(), __old_finish, + __new_finish, _M_get_Tp_allocator()); + } + else +#endif + { + __new_finish + = std::__uninitialized_move_if_noexcept_a + (__old_start, __position.base(), + __new_start, _M_get_Tp_allocator()); + + ++__new_finish; + + __new_finish + = std::__uninitialized_move_if_noexcept_a + (__position.base(), __old_finish, + __new_finish, _M_get_Tp_allocator()); + } } __catch(...) { if (!__new_finish) _Alloc_traits::destroy(this->_M_impl, __new_start + __elems_before); else std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } +#if __cplusplus >= 201703L + if constexpr (!__use_relocate) +#endif + std::_Destroy(__old_start, __old_finish, _M_get_Tp_allocator()); _GLIBCXX_ASAN_ANNOTATE_REINIT; - std::_Destroy(__old_start, __old_finish, _M_get_Tp_allocator()); _M_deallocate(__old_start, this->_M_impl._M_end_of_storage - __old_start); this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_finish; this->_M_impl._M_end_of_storage = __new_start + __len; } template<typename _Tp, typename _Alloc> void vector<_Tp, _Alloc>:: @@ -555,23 +590,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER if (!__new_finish) std::_Destroy(__new_start + __elems_before, __new_start + __elems_before + __n, _M_get_Tp_allocator()); else std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } - _GLIBCXX_ASAN_ANNOTATE_REINIT; std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); + _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_finish; this->_M_impl._M_end_of_storage = __new_start + __len; } } } @@ -596,41 +631,62 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER this->_M_impl._M_finish = std::__uninitialized_default_n_a(this->_M_impl._M_finish, __n, _M_get_Tp_allocator()); _GLIBCXX_ASAN_ANNOTATE_GREW(__n); } else { const size_type __len = _M_check_len(__n, "vector::_M_default_append"); pointer __new_start(this->_M_allocate(__len)); - pointer __destroy_from = pointer(); - __try +#if __cplusplus >= 201703L + if constexpr (__use_relocate) { - std::__uninitialized_default_n_a(__new_start + __size, - __n, _M_get_Tp_allocator()); - __destroy_from = __new_start + __size; - std::__uninitialized_move_if_noexcept_a( - this->_M_impl._M_start, this->_M_impl._M_finish, - __new_start, _M_get_Tp_allocator()); + __try + { + std::__uninitialized_default_n_a(__new_start + __size, + __n, _M_get_Tp_allocator()); + } + __catch(...) + { + _M_deallocate(__new_start, __len); + __throw_exception_again; + } + std::__relocate_a(this->_M_impl._M_start, + this->_M_impl._M_finish, + __new_start, _M_get_Tp_allocator()); } - __catch(...) + else +#endif { - if (__destroy_from) - std::_Destroy(__destroy_from, __destroy_from + __n, - _M_get_Tp_allocator()); - _M_deallocate(__new_start, __len); - __throw_exception_again; + pointer __destroy_from = pointer(); + __try + { + std::__uninitialized_default_n_a(__new_start + __size, + __n, _M_get_Tp_allocator()); + __destroy_from = __new_start + __size; + std::__uninitialized_move_if_noexcept_a( + this->_M_impl._M_start, this->_M_impl._M_finish, + __new_start, _M_get_Tp_allocator()); + } + __catch(...) + { + if (__destroy_from) + std::_Destroy(__destroy_from, __destroy_from + __n, + _M_get_Tp_allocator()); + _M_deallocate(__new_start, __len); + __throw_exception_again; + } + std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, + _M_get_Tp_allocator()); } _GLIBCXX_ASAN_ANNOTATE_REINIT; - std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, - _M_get_Tp_allocator()); _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_start + __size + __n; this->_M_impl._M_end_of_storage = __new_start + __len; } } } @@ -735,23 +791,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER (__position.base(), this->_M_impl._M_finish, __new_finish, _M_get_Tp_allocator()); } __catch(...) { std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } - _GLIBCXX_ASAN_ANNOTATE_REINIT; std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); + _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_finish; this->_M_impl._M_end_of_storage = __new_start + __len; } } } Index: include/ext/alloc_traits.h =================================================================== --- include/ext/alloc_traits.h (revision 264027) +++ include/ext/alloc_traits.h (working copy) @@ -73,29 +73,32 @@ template<typename _Alloc, typename = typ template<typename _Ptr> using __is_custom_pointer = std::__and_<std::is_same<pointer, _Ptr>, std::__not_<std::is_pointer<_Ptr>>>; public: // overload construct for non-standard pointer types template<typename _Ptr, typename... _Args> static typename std::enable_if<__is_custom_pointer<_Ptr>::value>::type construct(_Alloc& __a, _Ptr __p, _Args&&... __args) + noexcept(noexcept(_Base_type::construct(__a, std::__to_address(__p), + std::forward<_Args>(__args)...))) { _Base_type::construct(__a, std::__to_address(__p), std::forward<_Args>(__args)...); } // overload destroy for non-standard pointer types template<typename _Ptr> static typename std::enable_if<__is_custom_pointer<_Ptr>::value>::type destroy(_Alloc& __a, _Ptr __p) + noexcept(noexcept(_Base_type::destroy(__a, std::__to_address(__p)))) { _Base_type::destroy(__a, std::__to_address(__p)); } static _Alloc _S_select_on_copy(const _Alloc& __a) { return _Base_type::select_on_container_copy_construction(__a); } static void _S_on_swap(_Alloc& __a, _Alloc& __b) { std::__alloc_on_swap(__a, __b); } static constexpr bool _S_propagate_on_copy_assign() { return _Base_type::propagate_on_container_copy_assignment::value; } Index: include/ext/malloc_allocator.h =================================================================== --- include/ext/malloc_allocator.h (revision 264027) +++ include/ext/malloc_allocator.h (working copy) @@ -138,25 +138,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { std::free(static_cast<void*>(__p)); } size_type max_size() const _GLIBCXX_USE_NOEXCEPT { return size_t(-1) / sizeof(_Tp); } #if __cplusplus >= 201103L template<typename _Up, typename... _Args> void construct(_Up* __p, _Args&&... __args) + noexcept(noexcept(::new((void *)__p) + _Up(std::forward<_Args>(__args)...))) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } template<typename _Up> void - destroy(_Up* __p) { __p->~_Up(); } + destroy(_Up* __p) + noexcept(noexcept(__p->~_Up())) + { __p->~_Up(); } #else // _GLIBCXX_RESOLVE_LIB_DEFECTS // 402. wrong new expression in [some_] allocator::construct void construct(pointer __p, const _Tp& __val) { ::new((void *)__p) value_type(__val); } void destroy(pointer __p) { __p->~_Tp(); } #endif Index: include/ext/new_allocator.h =================================================================== --- include/ext/new_allocator.h (revision 264027) +++ include/ext/new_allocator.h (working copy) @@ -129,25 +129,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } size_type max_size() const _GLIBCXX_USE_NOEXCEPT { return size_t(-1) / sizeof(_Tp); } #if __cplusplus >= 201103L template<typename _Up, typename... _Args> void construct(_Up* __p, _Args&&... __args) + noexcept(noexcept(::new((void *)__p) + _Up(std::forward<_Args>(__args)...))) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } template<typename _Up> void - destroy(_Up* __p) { __p->~_Up(); } + destroy(_Up* __p) + noexcept(noexcept( __p->~_Up())) + { __p->~_Up(); } #else // _GLIBCXX_RESOLVE_LIB_DEFECTS // 402. wrong new expression in [some_] allocator::construct void construct(pointer __p, const _Tp& __val) { ::new((void *)__p) _Tp(__val); } void destroy(pointer __p) { __p->~_Tp(); } #endif