This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
Relocation (= move+destroy)
- From: Marc Glisse <marc dot glisse at inria dot fr>
- To: gcc-patches at gcc dot gnu dot org, libstdc++ at gcc dot gnu dot org
- Date: Sat, 1 Sep 2018 17:00:01 +0200 (CEST)
- Subject: Relocation (= move+destroy)
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.glisse@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