On Fri, 18 Apr 2025 at 10:03, Tomasz Kamiński <tkami...@redhat.com> wrote: > > This patch adds a _Guard_nodes scope guard nested to the _Deque_base, > that deallocates the range of nodes, and replaces __try/__catch block > with approparietly constructed guard object.
"appropriately" > > libstdc++-v3/ChangeLog: > > * include/bits/deque.tcc (_Deque_base<_Tp, _Alloc>::_Guard_nodes): > Define. There's no need for the template argument list here, just "_Deque_base" is unambiguous (there's no partial or explicit specialization that could be disambiguated with template argument lists). And just "deque" below. > (_Deque_base<_Tp, _Alloc>::_M_create_nodes): Moved defintion from > stl_deque.h > and replace __try/__catch with _Guard_nodes scope object. > (deque<_Tp, _Alloc>::_M_fill_insert, deque<_Tp, > _Alloc>::_M_default_append) > (deque<_Tp, _Alloc>::_M_push_back_aux, deque<_Tp, > _Alloc>::_M_push_front_aux) > (deque<_Tp, _Alloc>::_M_range_prepend, deque<_Tp, > _Alloc>::_M_range_append) > (deque<_Tp, _Alloc>::_M_insert_aux): Replace __try/__catch with > _Guard_nodes > scope object. > (deque<_Tp, _Alloc>::_M_new_elements_at_back) > (deque<_Tp, _Alloc>::_M_new_elements_at_back): Use _M_create_nodes. > * include/bits/stl_deque.h (_Deque_base<_Tp, _Alloc>::_Guard_nodes): > Declare. > (_Deque_base<_Tp, _Alloc)::_M_create_nodes): Move defintion to > deque.tcc. > (deque<_Tp, _Alloc>::_Guard_nodes): Add typedef, so name is found by > lookup. > --- > Testing x86_64-linux, default test configuration passed. > OK for trunk? > > libstdc++-v3/include/bits/deque.tcc | 424 ++++++++++++-------------- > libstdc++-v3/include/bits/stl_deque.h | 20 +- > 2 files changed, 196 insertions(+), 248 deletions(-) > > diff --git a/libstdc++-v3/include/bits/deque.tcc > b/libstdc++-v3/include/bits/deque.tcc > index dabb6ec5365..b70eed69294 100644 > --- a/libstdc++-v3/include/bits/deque.tcc > +++ b/libstdc++-v3/include/bits/deque.tcc > @@ -63,6 +63,40 @@ namespace std _GLIBCXX_VISIBILITY(default) > _GLIBCXX_BEGIN_NAMESPACE_VERSION > _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > > + template<typename _Tp, typename _Alloc> > + struct No new line here, just "struct _Deque_base...". > + _Deque_base<_Tp, _Alloc>::_Guard_nodes > + { > + _Guard_nodes(_Deque_base& __self, > + _Map_pointer __first, _Map_pointer __last) > + : _M_self(__self), _M_first(__first), _M_last(__last) > + { } > + > + ~_Guard_nodes() > + { _M_self._M_destroy_nodes(_M_first, _M_last); } > + > + void _M_disarm() > + { _M_first = _M_last; } > + > + _Deque_base& _M_self; > + _Map_pointer _M_first; > + _Map_pointer _M_last; > + > + private: > + _Guard_nodes(_Guard_nodes const&); > + }; > + > + template<typename _Tp, typename _Alloc> > + void > + _Deque_base<_Tp, _Alloc>:: > + _M_create_nodes(_Map_pointer __nstart, _Map_pointer __nfinish) > + { > + _Guard_nodes __guard(*this, __nstart, __nstart); > + for (_Map_pointer& __cur = __guard._M_last; __cur < __nfinish; ++__cur) > + *__cur = this->_M_allocate_node(); > + __guard._M_disarm(); > + } > + > #if __cplusplus >= 201103L > template <typename _Tp, typename _Alloc> > void > @@ -310,35 +344,25 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > if (__pos._M_cur == this->_M_impl._M_start._M_cur) > { > iterator __new_start = _M_reserve_elements_at_front(__n); > - __try > - { > - std::__uninitialized_fill_a(__new_start, this->_M_impl._M_start, > - __x, _M_get_Tp_allocator()); > - this->_M_impl._M_start = __new_start; > - } > - __catch(...) > - { > - _M_destroy_nodes(__new_start._M_node, > - this->_M_impl._M_start._M_node); > - __throw_exception_again; > - } > + _Guard_nodes __guard(*this, __new_start._M_node, > + this->_M_impl._M_start._M_node); > + > + std::__uninitialized_fill_a(__new_start, this->_M_impl._M_start, > + __x, _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_start = __new_start; > } > else if (__pos._M_cur == this->_M_impl._M_finish._M_cur) > { > iterator __new_finish = _M_reserve_elements_at_back(__n); > - __try > - { > - std::__uninitialized_fill_a(this->_M_impl._M_finish, > - __new_finish, __x, > - _M_get_Tp_allocator()); > - this->_M_impl._M_finish = __new_finish; > - } > - __catch(...) > - { > - _M_destroy_nodes(this->_M_impl._M_finish._M_node + 1, > - __new_finish._M_node + 1); > - __throw_exception_again; > - } > + _Guard_nodes __guard(*this, this->_M_impl._M_finish._M_node + 1, > + __new_finish._M_node + 1); > + > + std::__uninitialized_fill_a(this->_M_impl._M_finish, > + __new_finish, __x, > + _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_finish = __new_finish; > } > else > _M_insert_aux(__pos, __n, __x); > @@ -350,23 +374,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > deque<_Tp, _Alloc>:: > _M_default_append(size_type __n) > { > - if (__n) > - { > - iterator __new_finish = _M_reserve_elements_at_back(__n); > - __try > - { > - std::__uninitialized_default_a(this->_M_impl._M_finish, > - __new_finish, > - _M_get_Tp_allocator()); > - this->_M_impl._M_finish = __new_finish; > - } > - __catch(...) > - { > - _M_destroy_nodes(this->_M_impl._M_finish._M_node + 1, > - __new_finish._M_node + 1); > - __throw_exception_again; > - } > - } > + if (!__n) > + return; > + > + iterator __new_finish = _M_reserve_elements_at_back(__n); > + _Guard_nodes __guard(*this, this->_M_impl._M_finish._M_node + 1, > + __new_finish._M_node + 1); > + > + std::__uninitialized_default_a(this->_M_impl._M_finish, > + __new_finish, > + _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_finish = __new_finish; > } > > template <typename _Tp, typename _Alloc> > @@ -495,24 +514,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > > _M_reserve_map_at_back(); > *(this->_M_impl._M_finish._M_node + 1) = this->_M_allocate_node(); > - __try > - { > + _Guard_nodes __guard(*this, this->_M_impl._M_finish._M_node, > + this->_M_impl._M_finish._M_node + 1); > #if __cplusplus >= 201103L > - _Alloc_traits::construct(this->_M_impl, > - this->_M_impl._M_finish._M_cur, > - std::forward<_Args>(__args)...); > + _Alloc_traits::construct(this->_M_impl, > + this->_M_impl._M_finish._M_cur, > + std::forward<_Args>(__args)...); > #else > - this->_M_impl.construct(this->_M_impl._M_finish._M_cur, __t); > + this->_M_impl.construct(this->_M_impl._M_finish._M_cur, __t); > #endif > - > this->_M_impl._M_finish._M_set_node(this->_M_impl._M_finish._M_node > - + 1); > - this->_M_impl._M_finish._M_cur = this->_M_impl._M_finish._M_first; > - } > - __catch(...) > - { > - _M_deallocate_node(*(this->_M_impl._M_finish._M_node + 1)); This deallocates _M_node+1 but the guard object guards [_M_node,_M_node+1). I think there's an off-by-one error here. > - __throw_exception_again; > - } > + __guard._M_disarm(); > + this->_M_impl._M_finish._M_set_node(this->_M_impl._M_finish._M_node + > 1); > + this->_M_impl._M_finish._M_cur = this->_M_impl._M_finish._M_first; > } > > // Called only if _M_impl._M_start._M_cur == _M_impl._M_start._M_first. > @@ -534,25 +547,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > > _M_reserve_map_at_front(); > *(this->_M_impl._M_start._M_node - 1) = this->_M_allocate_node(); > - __try > - { > - this->_M_impl._M_start._M_set_node(this->_M_impl._M_start._M_node > - - 1); > - this->_M_impl._M_start._M_cur = this->_M_impl._M_start._M_last - > 1; > + _Guard_nodes __guard(*this, this->_M_impl._M_start._M_node - 1, > + this->_M_impl._M_start._M_node); > + > + iterator __new_start; > + __new_start._M_set_node(this->_M_impl._M_start._M_node - 1); > + __new_start._M_cur = __new_start._M_last - 1; > #if __cplusplus >= 201103L > - _Alloc_traits::construct(this->_M_impl, > - this->_M_impl._M_start._M_cur, > - std::forward<_Args>(__args)...); > + _Alloc_traits::construct(this->_M_impl, > + __new_start._M_cur, > + std::forward<_Args>(__args)...); > #else > - this->_M_impl.construct(this->_M_impl._M_start._M_cur, __t); > + this->_M_impl.construct(__new_start._M_cur, __t); > #endif > - } > - __catch(...) > - { > - ++this->_M_impl._M_start; > - _M_deallocate_node(*(this->_M_impl._M_start._M_node - 1)); > - __throw_exception_again; > - } > + __guard._M_disarm(); > + this->_M_impl._M_start = __new_start; > } > > // Called only if _M_impl._M_finish._M_cur == _M_impl._M_finish._M_first. > @@ -591,18 +600,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > size_type __n) > { > iterator __new_start = _M_reserve_elements_at_front(__n); > - __try > - { > - std::__uninitialized_copy_a(_GLIBCXX_MOVE(__first), __last, > - __new_start, _M_get_Tp_allocator()); > - this->_M_impl._M_start = __new_start; > - } > - __catch(...) > - { > - _M_destroy_nodes(__new_start._M_node, > - this->_M_impl._M_start._M_node); > - __throw_exception_again; > - } > + _Guard_nodes __guard(*this, __new_start._M_node, > + this->_M_impl._M_start._M_node); > + > + std::__uninitialized_copy_a(_GLIBCXX_MOVE(__first), __last, > + __new_start, _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_start = __new_start; > } > > template <typename _Tp, typename _Alloc> > @@ -613,19 +617,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > size_type __n) > { > iterator __new_finish = _M_reserve_elements_at_back(__n); > - __try > - { > - std::__uninitialized_copy_a(_GLIBCXX_MOVE(__first), __last, > - this->_M_impl._M_finish, > - _M_get_Tp_allocator()); > - this->_M_impl._M_finish = __new_finish; > - } > - __catch(...) > - { > - _M_destroy_nodes(this->_M_impl._M_finish._M_node + 1, > - __new_finish._M_node + 1); > - __throw_exception_again; > - } > + _Guard_nodes __guard(*this, this->_M_impl._M_finish._M_node + 1, > + __new_finish._M_node + 1); > + > + std::__uninitialized_copy_a(_GLIBCXX_MOVE(__first), __last, > + this->_M_impl._M_finish, > + _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_finish = __new_finish; > } > > template <typename _Tp, typename _Alloc> > @@ -712,35 +711,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > iterator __new_start = _M_reserve_elements_at_front(__n); > iterator __old_start = this->_M_impl._M_start; > __pos = this->_M_impl._M_start + __elems_before; > - __try > + > + _Guard_nodes __guard(*this, __new_start._M_node, > + this->_M_impl._M_start._M_node); > + if (__elems_before >= difference_type(__n)) > { > - if (__elems_before >= difference_type(__n)) > - { > - iterator __start_n = (this->_M_impl._M_start > - + difference_type(__n)); > - std::__uninitialized_move_a(this->_M_impl._M_start, > - __start_n, __new_start, > - _M_get_Tp_allocator()); > - this->_M_impl._M_start = __new_start; > - _GLIBCXX_MOVE3(__start_n, __pos, __old_start); > - std::fill(__pos - difference_type(__n), __pos, __x_copy); > - } > - else > - { > - std::__uninitialized_move_fill(this->_M_impl._M_start, > - __pos, __new_start, > - this->_M_impl._M_start, > - __x_copy, > - _M_get_Tp_allocator()); > - this->_M_impl._M_start = __new_start; > - std::fill(__old_start, __pos, __x_copy); > - } > + iterator __start_n = (this->_M_impl._M_start > + + difference_type(__n)); > + std::__uninitialized_move_a(this->_M_impl._M_start, > + __start_n, __new_start, > + _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_start = __new_start; > + _GLIBCXX_MOVE3(__start_n, __pos, __old_start); > + std::fill(__pos - difference_type(__n), __pos, __x_copy); > } > - __catch(...) > + else > { > - _M_destroy_nodes(__new_start._M_node, > - this->_M_impl._M_start._M_node); > - __throw_exception_again; > + std::__uninitialized_move_fill(this->_M_impl._M_start, > + __pos, __new_start, > + this->_M_impl._M_start, > + __x_copy, > + _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_start = __new_start; > + std::fill(__old_start, __pos, __x_copy); > } > } > else > @@ -750,36 +745,32 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > const difference_type __elems_after = > difference_type(__length) - __elems_before; > __pos = this->_M_impl._M_finish - __elems_after; > - __try > + > + _Guard_nodes __guard(*this, this->_M_impl._M_finish._M_node + 1, > + __new_finish._M_node + 1); > + if (__elems_after > difference_type(__n)) > { > - if (__elems_after > difference_type(__n)) > - { > - iterator __finish_n = (this->_M_impl._M_finish > - - difference_type(__n)); > - std::__uninitialized_move_a(__finish_n, > - this->_M_impl._M_finish, > - this->_M_impl._M_finish, > - _M_get_Tp_allocator()); > - this->_M_impl._M_finish = __new_finish; > - _GLIBCXX_MOVE_BACKWARD3(__pos, __finish_n, __old_finish); > - std::fill(__pos, __pos + difference_type(__n), __x_copy); > - } > - else > - { > - std::__uninitialized_fill_move(this->_M_impl._M_finish, > - __pos + difference_type(__n), > - __x_copy, __pos, > - this->_M_impl._M_finish, > - _M_get_Tp_allocator()); > - this->_M_impl._M_finish = __new_finish; > - std::fill(__pos, __old_finish, __x_copy); > - } > + iterator __finish_n = (this->_M_impl._M_finish > + - difference_type(__n)); > + std::__uninitialized_move_a(__finish_n, > + this->_M_impl._M_finish, > + this->_M_impl._M_finish, > + _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_finish = __new_finish; > + _GLIBCXX_MOVE_BACKWARD3(__pos, __finish_n, __old_finish); > + std::fill(__pos, __pos + difference_type(__n), __x_copy); > } > - __catch(...) > + else > { > - _M_destroy_nodes(this->_M_impl._M_finish._M_node + 1, > - __new_finish._M_node + 1); > - __throw_exception_again; > + std::__uninitialized_fill_move(this->_M_impl._M_finish, > + __pos + difference_type(__n), > + __x_copy, __pos, > + this->_M_impl._M_finish, > + _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_finish = __new_finish; > + std::fill(__pos, __old_finish, __x_copy); > } > } > } > @@ -799,36 +790,33 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > iterator __new_start = _M_reserve_elements_at_front(__n); > iterator __old_start = this->_M_impl._M_start; > __pos = this->_M_impl._M_start + __elemsbefore; > - __try > + > + _Guard_nodes __guard(*this, __new_start._M_node, > + this->_M_impl._M_start._M_node); > + > + if (__elemsbefore >= difference_type(__n)) > { > - if (__elemsbefore >= difference_type(__n)) > - { > - iterator __start_n = (this->_M_impl._M_start > - + difference_type(__n)); > - std::__uninitialized_move_a(this->_M_impl._M_start, > - __start_n, __new_start, > - _M_get_Tp_allocator()); > - this->_M_impl._M_start = __new_start; > - _GLIBCXX_MOVE3(__start_n, __pos, __old_start); > - std::copy(__first, __last, __pos - difference_type(__n)); > - } > - else > - { > - _ForwardIterator __mid = __first; > - std::advance(__mid, difference_type(__n) - __elemsbefore); > - std::__uninitialized_move_copy(this->_M_impl._M_start, > - __pos, __first, __mid, > - __new_start, > - _M_get_Tp_allocator()); > - this->_M_impl._M_start = __new_start; > - std::copy(__mid, __last, __old_start); > - } > + iterator __start_n = (this->_M_impl._M_start > + + difference_type(__n)); > + std::__uninitialized_move_a(this->_M_impl._M_start, > + __start_n, __new_start, > + _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_start = __new_start; > + _GLIBCXX_MOVE3(__start_n, __pos, __old_start); > + std::copy(__first, __last, __pos - difference_type(__n)); > } > - __catch(...) > + else > { > - _M_destroy_nodes(__new_start._M_node, > - this->_M_impl._M_start._M_node); > - __throw_exception_again; > + _ForwardIterator __mid = __first; > + std::advance(__mid, difference_type(__n) - __elemsbefore); > + std::__uninitialized_move_copy(this->_M_impl._M_start, > + __pos, __first, __mid, > + __new_start, > + _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_start = __new_start; > + std::copy(__mid, __last, __old_start); > } > } > else > @@ -838,37 +826,33 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > const difference_type __elemsafter = > difference_type(__length) - __elemsbefore; > __pos = this->_M_impl._M_finish - __elemsafter; > - __try > + > + _Guard_nodes __guard(*this, this->_M_impl._M_finish._M_node + 1, > + __new_finish._M_node + 1); > + if (__elemsafter > difference_type(__n)) > { > - if (__elemsafter > difference_type(__n)) > - { > - iterator __finish_n = (this->_M_impl._M_finish > - - difference_type(__n)); > - std::__uninitialized_move_a(__finish_n, > - this->_M_impl._M_finish, > - this->_M_impl._M_finish, > - _M_get_Tp_allocator()); > - this->_M_impl._M_finish = __new_finish; > - _GLIBCXX_MOVE_BACKWARD3(__pos, __finish_n, __old_finish); > - std::copy(__first, __last, __pos); > - } > - else > - { > - _ForwardIterator __mid = __first; > - std::advance(__mid, __elemsafter); > - std::__uninitialized_copy_move(__mid, __last, __pos, > - this->_M_impl._M_finish, > - this->_M_impl._M_finish, > - _M_get_Tp_allocator()); > - this->_M_impl._M_finish = __new_finish; > - std::copy(__first, __mid, __pos); > - } > + iterator __finish_n = (this->_M_impl._M_finish > + - difference_type(__n)); > + std::__uninitialized_move_a(__finish_n, > + this->_M_impl._M_finish, > + this->_M_impl._M_finish, > + _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_finish = __new_finish; > + _GLIBCXX_MOVE_BACKWARD3(__pos, __finish_n, __old_finish); > + std::copy(__first, __last, __pos); > } > - __catch(...) > + else > { > - _M_destroy_nodes(this->_M_impl._M_finish._M_node + 1, > - __new_finish._M_node + 1); > - __throw_exception_again; > + _ForwardIterator __mid = __first; > + std::advance(__mid, __elemsafter); > + std::__uninitialized_copy_move(__mid, __last, __pos, > + this->_M_impl._M_finish, > + this->_M_impl._M_finish, > + _M_get_Tp_allocator()); > + __guard._M_disarm(); > + this->_M_impl._M_finish = __new_finish; > + std::copy(__first, __mid, __pos); > } > } > } > @@ -1057,18 +1041,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > const size_type __new_nodes = ((__new_elems + _S_buffer_size() - 1) > / _S_buffer_size()); > _M_reserve_map_at_front(__new_nodes); > - size_type __i; > - __try > - { > - for (__i = 1; __i <= __new_nodes; ++__i) > - *(this->_M_impl._M_start._M_node - __i) = > this->_M_allocate_node(); > - } > - __catch(...) > - { > - for (size_type __j = 1; __j < __i; ++__j) > - _M_deallocate_node(*(this->_M_impl._M_start._M_node - __j)); > - __throw_exception_again; > - } > + _M_create_nodes(this->_M_impl._M_start._M_node - __new_nodes, > + this->_M_impl._M_start._M_node); > } > > template <typename _Tp, typename _Alloc> > @@ -1082,18 +1056,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > const size_type __new_nodes = ((__new_elems + _S_buffer_size() - 1) > / _S_buffer_size()); > _M_reserve_map_at_back(__new_nodes); > - size_type __i; > - __try > - { > - for (__i = 1; __i <= __new_nodes; ++__i) > - *(this->_M_impl._M_finish._M_node + __i) = > this->_M_allocate_node(); > - } > - __catch(...) > - { > - for (size_type __j = 1; __j < __i; ++__j) > - _M_deallocate_node(*(this->_M_impl._M_finish._M_node + __j)); > - __throw_exception_again; > - } > + _M_create_nodes(_M_impl._M_finish._M_node + 1, > + _M_impl._M_finish._M_node + __new_nodes + 1); > } > > template <typename _Tp, typename _Alloc> > diff --git a/libstdc++-v3/include/bits/stl_deque.h > b/libstdc++-v3/include/bits/stl_deque.h > index 8d8ee575a26..73e48299938 100644 > --- a/libstdc++-v3/include/bits/stl_deque.h > +++ b/libstdc++-v3/include/bits/stl_deque.h > @@ -612,6 +612,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > void _M_destroy_nodes(_Map_pointer __nstart, > _Map_pointer __nfinish) _GLIBCXX_NOEXCEPT; > enum { _S_initial_map_size = 8 }; > + struct _Guard_nodes; > > _Deque_impl _M_impl; > }; > @@ -675,24 +676,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > % __deque_buf_size(sizeof(_Tp))); > } > > - template<typename _Tp, typename _Alloc> > - void > - _Deque_base<_Tp, _Alloc>:: > - _M_create_nodes(_Map_pointer __nstart, _Map_pointer __nfinish) > - { > - _Map_pointer __cur; > - __try > - { > - for (__cur = __nstart; __cur < __nfinish; ++__cur) > - *__cur = this->_M_allocate_node(); > - } > - __catch(...) > - { > - _M_destroy_nodes(__nstart, __cur); > - __throw_exception_again; > - } > - } > - > template<typename _Tp, typename _Alloc> > void > _Deque_base<_Tp, _Alloc>:: > @@ -812,6 +795,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > typedef typename _Base::_Tp_alloc_type _Tp_alloc_type; > typedef typename _Base::_Alloc_traits _Alloc_traits; > typedef typename _Base::_Map_pointer _Map_pointer; > + typedef typename _Base::_Guard_nodes _Guard_nodes; > > public: > typedef _Tp value_type; > -- > 2.49.0 >