Add fancy pointer support in std::map/set

Message ID 0480d8b9-6f98-4528-a0c1-a8d15806d410@gmail.com
State New
Headers
Series Add fancy pointer support in std::map/set |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Patch failed to apply

Commit Message

François Dumont Aug. 1, 2024, 5:26 p.m. UTC
  Hi

Here is a proposal to add fancy pointer support in std::_Rb_tree container.

As you'll see there are still several usages of 
pointer_traits<>::pointer_to. The ones in _M_header_ptr() are 
unavoidable. The ones to extract a node or to return a node to the 
allocator are more questionable. Are they fine ? Is there another way to 
mimic the static_cast<_Link_type> that can be done on raw pointers with 
fancy pointers ?

A solution to this (potential) issue would be to:

1. Store _Node_type pointer in the data structure rather than _Node_base 
pointer. But then _Rb_tree_pheader _M_left/_M_right could not be 
initialized with a pointer to _M_parent which is only a _Node_base 
instance. We would then need to init it to nullptr but I haven't check 
impact on code and more importantly it would be an abi breaking change.

2. Still store _Node_type pointer in the data structure but then make 
_M_header a _Node_type instance. In this case memory area of the 
value_type would just be lost bytes.


     libstdc++: Add fancy pointer support in std::[multi][map/set]

     Support fancy allocator pointer type in std::_Rb_tree<>.

     In case of fancy pointer type the container is now storing the 
pointer to
     _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.

     Many methods are adapted to take and return _Base_ptr in place of 
_Link_type.

     libstdc++-v3/ChangeLog:

             * include/bits/stl_tree.h
             (_Rb_tree_pnode_base<>): New.
             (_Rb_tree_node_base): Inherit from latter.
             (_Rb_tree_pheader): New.
             (_Rb_tree_header): Inherit from latter.
             (_Rb_tree_node_val): New.
             (_Rb_tree_node): Inherit from latter.
             (_Rb_tree_pnode): New.
             (_Rb_tree_helpers): New.
             (_Rb_tree_piterator): New.
             (_Rb_tree_const_piterator): New.
             (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
             (_Rb_tree): Adapt to generalize usage of _Base_ptr in place 
of _Link_type.
             * testsuite/23_containers/map/allocator/ext_ptr.cc: New 
test case.
             * testsuite/23_containers/multimap/allocator/ext_ptr.cc: 
New test case.
             * testsuite/23_containers/multiset/allocator/ext_ptr.cc: 
New test case.
             * testsuite/23_containers/set/allocator/ext_ptr.cc: New 
test case.

Tested under Linux x64.

François
  

Comments

Jonathan Wakely Oct. 8, 2024, 9:50 p.m. UTC | #1
On Thu, 1 Aug 2024 at 18:28, François Dumont <frs.dumont@gmail.com> wrote:

> Hi
>
> Here is a proposal to add fancy pointer support in std::_Rb_tree container.
>
> As you'll see there are still several usages of
> pointer_traits<>::pointer_to. The ones in _M_header_ptr() are
> unavoidable.


Yes, those are necessary.

The pointer_to use in _M_const_cast could be simplified by adding a
_M_self_ptr() member to _Rb_tree_pnode_base:

  _Base_ptr
  _M_self_ptr() _GLIBCXX_NOEXCEPT
  { return pointer_traits<_Base_ptr>::pointer_to(*this); }

  _Base_ptr
  _M_self_ptr_nc() const _GLIBCXX_NOEXCEPT
  {
    auto __self = const_cast<_Rb_tree_pnode_base*>(this);
    return __self->_M_self_ptr();
  }

 _Const_Base_ptr
  _M_self_ptr() const _GLIBCXX_NOEXCEPT
  { return pointer_traits<_Const_Base_ptr>::pointer_to(*this); }

Then _M_const_cast would do:

return iterator(_M_node->_M_self_ptr_nc());



> The ones to extract a node or to return a node to the
> allocator are more questionable. Are they fine ? Is there another way to
> mimic the static_cast<_Link_type> that can be done on raw pointers with
> fancy pointers ?
>

I think you can just do static_cast<_Link_type>(_M_node->_M_self_ptr())
i.e. convert the _Base_ptr to the derived _Link_type
(we should really rename _Link_type to _Node_ptr, I keep getting confused
by the fact that the actual links between nodes are _Base_ptr, so what does
_Link_type mean now?!)

Alternatively, you could add a _M_node_ptr() to the _Rb_tree_pnode_base
type:

template<typename _NodeT>
  __ptr_rebind<_BasePtr, _NodeT>
  _M_node_ptr() _GLIBCXX_NOEXCEPT
  {
    auto __node = static_cast<_NodeT*>(this);
    using _Node_ptr = __ptr_rebind<_BasePtr, _NodeT>;
    return pointer_traits<_Node_ptr>::pointer_to(*__node);
  }

Then:
  auto __np = __p->template _M_node_ptr<_Node_type>()
  _Alloc_traits::deallocate(_M_get_Node_allocator(), __np, 1);


Some more general comments:

_Rb_tree_pnode_base and the iterators could just take one template
argument, and use __ptr_rebind to get the const pointer from the non-const
pointer. We can add a helper to do that:

template<typename _Ptr> struct __add_const_to_ptr;
#if __cplusplus >= 201103L
template<typename _Ptr>
  using __add_const_to_ptr = __ptr_rebind<_Ptr, const typename
pointer_traits<_Ptr>::element_type>;
#else
template<typename _Ptr> struct __add_const_to_ptr;
template<typename _Tp>
  struct __add_const_to_ptr<_Tp*>
  { typedef const _Tp* __type; };
#endif

This will let you get the FooCPtr type from the FooPtr type.

A traits class could replace all the __conditional_t uses:

#if __cplusplus >= 201103L
template<typename _ValPtr>
struct _Rb_tree_node_traits
{
  using _Base = _Rb_tree_pnode_base<_ValPtr>;
  using _Node_type = _Rb_tree_pnode<_ValPtr>;
  using _Base_ptr = __ptr_rebind<_ValPtr, _Base>;
  using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
  using iterator = _Rb_tree_piterator<_ValPtr>;
  using const_iterator = _Rb_tree_const_piterator<_ValPtr>;
};
#else
template<typename _ValPtr>
struct _Rb_tree_node_traits;
#endif

template<typename _Val>
struct _Rb_tree_node_traits<_Val*>
{
  typedef _Rb_tree_node_base _Base;
  typedef _Rb_tree_node<_Val> _Node_type;
  typedef _Base* _Base_ptr;
  typedef _Node* _Node_ptr;
  typedef _Rb_tree_iterator<_Val> iterator;
  typedef _Rb_tree_const_iterator<_Val> const_iterator;
};

I think this would get rid of a lot of the #if checks and the
__conditional_t uses.

Otherwise this looks really good, nice work!


>
> A solution to this (potential) issue would be to:
>
> 1. Store _Node_type pointer in the data structure rather than _Node_base
> pointer. But then _Rb_tree_pheader _M_left/_M_right could not be
> initialized with a pointer to _M_parent which is only a _Node_base
> instance. We would then need to init it to nullptr but I haven't check
> impact on code and more importantly it would be an abi breaking change.
>
> 2. Still store _Node_type pointer in the data structure but then make
> _M_header a _Node_type instance. In this case memory area of the
> value_type would just be lost bytes.
>
>
>      libstdc++: Add fancy pointer support in std::[multi][map/set]
>

Let's just say "maps and sets" or "RB trees" instead of the more cryptic
std::[multi][map/set].


>      Support fancy allocator pointer type in std::_Rb_tree<>.
>
>      In case of fancy pointer type the container is now storing the
> pointer to
>      _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.
>
>      Many methods are adapted to take and return _Base_ptr in place of
> _Link_type.
>
>      libstdc++-v3/ChangeLog:
>
>              * include/bits/stl_tree.h
>              (_Rb_tree_pnode_base<>): New.
>              (_Rb_tree_node_base): Inherit from latter.
>              (_Rb_tree_pheader): New.
>              (_Rb_tree_header): Inherit from latter.
>              (_Rb_tree_node_val): New.
>              (_Rb_tree_node): Inherit from latter.
>              (_Rb_tree_pnode): New.
>              (_Rb_tree_helpers): New.
>              (_Rb_tree_piterator): New.
>              (_Rb_tree_const_piterator): New.
>              (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
>              (_Rb_tree): Adapt to generalize usage of _Base_ptr in place
> of _Link_type.
>              * testsuite/23_containers/map/allocator/ext_ptr.cc: New
> test case.
>              * testsuite/23_containers/multimap/allocator/ext_ptr.cc:
> New test case.
>              * testsuite/23_containers/multiset/allocator/ext_ptr.cc:
> New test case.
>              * testsuite/23_containers/set/allocator/ext_ptr.cc: New
> test case.
>
> Tested under Linux x64.
>
> François
>
  
Jonathan Wakely Oct. 8, 2024, 9:55 p.m. UTC | #2
On Tue, 8 Oct 2024 at 22:50, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:

>
>
> On Thu, 1 Aug 2024 at 18:28, François Dumont <frs.dumont@gmail.com> wrote:
>
>> Hi
>>
>> Here is a proposal to add fancy pointer support in std::_Rb_tree
>> container.
>>
>> As you'll see there are still several usages of
>> pointer_traits<>::pointer_to. The ones in _M_header_ptr() are
>> unavoidable.
>
>
> Yes, those are necessary.
>
> The pointer_to use in _M_const_cast could be simplified by adding a
> _M_self_ptr() member to _Rb_tree_pnode_base:
>
>   _Base_ptr
>   _M_self_ptr() _GLIBCXX_NOEXCEPT
>   { return pointer_traits<_Base_ptr>::pointer_to(*this); }
>
>   _Base_ptr
>   _M_self_ptr_nc() const _GLIBCXX_NOEXCEPT
>   {
>     auto __self = const_cast<_Rb_tree_pnode_base*>(this);
>     return __self->_M_self_ptr();
>   }
>


You might not even need this const overload:


>
>  _Const_Base_ptr
>   _M_self_ptr() const _GLIBCXX_NOEXCEPT
>   { return pointer_traits<_Const_Base_ptr>::pointer_to(*this); }
>
>
>
  
Jonathan Wakely Oct. 8, 2024, 10:02 p.m. UTC | #3
On Tue, 8 Oct 2024 at 22:50, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:

>
>
> On Thu, 1 Aug 2024 at 18:28, François Dumont <frs.dumont@gmail.com> wrote:
>
>> Hi
>>
>> Here is a proposal to add fancy pointer support in std::_Rb_tree
>> container.
>>
>> As you'll see there are still several usages of
>> pointer_traits<>::pointer_to. The ones in _M_header_ptr() are
>> unavoidable.
>
>
> Yes, those are necessary.
>
> The pointer_to use in _M_const_cast could be simplified by adding a
> _M_self_ptr() member to _Rb_tree_pnode_base:
>
>   _Base_ptr
>   _M_self_ptr() _GLIBCXX_NOEXCEPT
>   { return pointer_traits<_Base_ptr>::pointer_to(*this); }
>

I don't think it's needed (because _M_const_cast is only in C++11 code, I
think?) but you could define that for both C++11 and C++98:

#if __cplusplus
  _Base_ptr
  _M_self_ptr() noexcept
  { return pointer_traits<_Base_ptr>::pointer_to(*this); }
#else
  _Base_ptr _M_self_ptr() { return this; }
#endif

Then this works for both (if you replace the 'auto'):


>   _Base_ptr
>   _M_self_ptr_nc() const _GLIBCXX_NOEXCEPT
>   {
>     auto __self = const_cast<_Rb_tree_pnode_base*>(this);
>     return __self->_M_self_ptr();
>   }
>
>  _Const_Base_ptr
>   _M_self_ptr() const _GLIBCXX_NOEXCEPT
>   { return pointer_traits<_Const_Base_ptr>::pointer_to(*this); }
>
> Then _M_const_cast would do:
>
> return iterator(_M_node->_M_self_ptr_nc());
>
>
>
>> The ones to extract a node or to return a node to the
>> allocator are more questionable. Are they fine ? Is there another way to
>> mimic the static_cast<_Link_type> that can be done on raw pointers with
>> fancy pointers ?
>>
>
> I think you can just do static_cast<_Link_type>(_M_node->_M_self_ptr())
> i.e. convert the _Base_ptr to the derived _Link_type
> (we should really rename _Link_type to _Node_ptr, I keep getting confused
> by the fact that the actual links between nodes are _Base_ptr, so what does
> _Link_type mean now?!)
>
> Alternatively, you could add a _M_node_ptr() to the _Rb_tree_pnode_base
> type:
>
> template<typename _NodeT>
>   __ptr_rebind<_BasePtr, _NodeT>
>   _M_node_ptr() _GLIBCXX_NOEXCEPT
>   {
>     auto __node = static_cast<_NodeT*>(this);
>     using _Node_ptr = __ptr_rebind<_BasePtr, _NodeT>;
>     return pointer_traits<_Node_ptr>::pointer_to(*__node);
>   }
>

You could add a C++98 version of this as well:

#if __cplusplus >= 201103L
 // as above
#else
  template<typename _NodeT>
  _NodeT*
  _M_node_ptr() _GLIBCXX_NOEXCEPT
  { return static_cast<_Node*>(this); }
#endif


Then the code to deallocate the node would not need to use #if:

_Link_type __np = __p->template _M_node_ptr<_Node_type>();
_Alloc_traits::deallocate(_M_get_Node_allocator(), __np, 1);




> Then:
>   auto __np = __p->template _M_node_ptr<_Node_type>()
>   _Alloc_traits::deallocate(_M_get_Node_allocator(), __np, 1);
>
>
> Some more general comments:
>
> _Rb_tree_pnode_base and the iterators could just take one template
> argument, and use __ptr_rebind to get the const pointer from the non-const
> pointer. We can add a helper to do that:
>
> template<typename _Ptr> struct __add_const_to_ptr;
> #if __cplusplus >= 201103L
> template<typename _Ptr>
>   using __add_const_to_ptr = __ptr_rebind<_Ptr, const typename
> pointer_traits<_Ptr>::element_type>;
> #else
> template<typename _Ptr> struct __add_const_to_ptr;
> template<typename _Tp>
>   struct __add_const_to_ptr<_Tp*>
>   { typedef const _Tp* __type; };
> #endif
>
> This will let you get the FooCPtr type from the FooPtr type.
>
> A traits class could replace all the __conditional_t uses:
>
> #if __cplusplus >= 201103L
> template<typename _ValPtr>
> struct _Rb_tree_node_traits
> {
>   using _Base = _Rb_tree_pnode_base<_ValPtr>;
>   using _Node_type = _Rb_tree_pnode<_ValPtr>;
>   using _Base_ptr = __ptr_rebind<_ValPtr, _Base>;
>   using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
>   using iterator = _Rb_tree_piterator<_ValPtr>;
>   using const_iterator = _Rb_tree_const_piterator<_ValPtr>;
> };
> #else
> template<typename _ValPtr>
> struct _Rb_tree_node_traits;
> #endif
>
> template<typename _Val>
> struct _Rb_tree_node_traits<_Val*>
> {
>   typedef _Rb_tree_node_base _Base;
>   typedef _Rb_tree_node<_Val> _Node_type;
>   typedef _Base* _Base_ptr;
>   typedef _Node* _Node_ptr;
>   typedef _Rb_tree_iterator<_Val> iterator;
>   typedef _Rb_tree_const_iterator<_Val> const_iterator;
> };
>
> I think this would get rid of a lot of the #if checks and the
> __conditional_t uses.
>
> Otherwise this looks really good, nice work!
>
>
>>
>> A solution to this (potential) issue would be to:
>>
>> 1. Store _Node_type pointer in the data structure rather than _Node_base
>> pointer. But then _Rb_tree_pheader _M_left/_M_right could not be
>> initialized with a pointer to _M_parent which is only a _Node_base
>> instance. We would then need to init it to nullptr but I haven't check
>> impact on code and more importantly it would be an abi breaking change.
>>
>> 2. Still store _Node_type pointer in the data structure but then make
>> _M_header a _Node_type instance. In this case memory area of the
>> value_type would just be lost bytes.
>>
>>
>>      libstdc++: Add fancy pointer support in std::[multi][map/set]
>>
>
> Let's just say "maps and sets" or "RB trees" instead of the more cryptic
> std::[multi][map/set].
>
>
>>      Support fancy allocator pointer type in std::_Rb_tree<>.
>>
>>      In case of fancy pointer type the container is now storing the
>> pointer to
>>      _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.
>>
>>      Many methods are adapted to take and return _Base_ptr in place of
>> _Link_type.
>>
>>      libstdc++-v3/ChangeLog:
>>
>>              * include/bits/stl_tree.h
>>              (_Rb_tree_pnode_base<>): New.
>>              (_Rb_tree_node_base): Inherit from latter.
>>              (_Rb_tree_pheader): New.
>>              (_Rb_tree_header): Inherit from latter.
>>              (_Rb_tree_node_val): New.
>>              (_Rb_tree_node): Inherit from latter.
>>              (_Rb_tree_pnode): New.
>>              (_Rb_tree_helpers): New.
>>              (_Rb_tree_piterator): New.
>>              (_Rb_tree_const_piterator): New.
>>              (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
>>              (_Rb_tree): Adapt to generalize usage of _Base_ptr in place
>> of _Link_type.
>>              * testsuite/23_containers/map/allocator/ext_ptr.cc: New
>> test case.
>>              * testsuite/23_containers/multimap/allocator/ext_ptr.cc:
>> New test case.
>>              * testsuite/23_containers/multiset/allocator/ext_ptr.cc:
>> New test case.
>>              * testsuite/23_containers/set/allocator/ext_ptr.cc: New
>> test case.
>>
>> Tested under Linux x64.
>>
>> François
>>
>
  
François Dumont Oct. 17, 2024, 7:52 p.m. UTC | #4
Here is an updated version that compiles, I think, all your feedbacks. 
It's much cleaner indeed.

It's also tested in C++98/17/23.

I'm surprised that we do not need to consider potential 
allocator::const_pointer. Is there a plan to deprecate it ?

And if not, should not alloc traits const_pointer be per default a 
rebind of pointer for const element_type like in the __add_const_to_ptr 
you made me add ? I can try to work on a patch for that if needed.

     libstdc++: Add fancy pointer support in map and set

     Support fancy allocator pointer type in std::_Rb_tree<>.

     In case of fancy pointer type the container is now storing the 
pointer to
     _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.

     Many methods are adapted to take and return _Base_ptr in place of 
_Link_type
     which has been renamed into _Node_ptr.

     libstdc++-v3/ChangeLog:

             * include/bits/stl_tree.h
             (__add_const_to_ptr<>): New.
             (_Rb_tree_ptr_traits<>): New.
             (_Rb_tree_pnode_base<>): New.
             (_Rb_tree_node_base): Inherit from latter.
             (_Rb_tree_pheader): New.
             (_Rb_tree_header): Inherit from latter.
             (_Rb_tree_node_val): New.
             (_Rb_tree_node): Inherit from latter.
             (_Rb_tree_pnode): New.
             (_Rb_tree_iterator<>::_Link_type): Rename into...
             (_Rb_tree_iterator<>::_Node_type): ...this.
             (_Rb_tree_const_iterator<>::_Link_type): Rename into...
             (_Rb_tree_const_iterator<>::_Node_type): ...this.
             (_Rb_tree_const_iterator<>::_M_const_cast()): Adapt to use
             _Rb_tree_pnode_base<>::_M_self_ptr_nc method.
             (_Rb_tree_helpers<>): New.
             (_Rb_tree_piterator): New.
             (_Rb_tree_const_piterator): New.
             (_Rb_tree_node_traits<>): New.
             (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
             (_Rb_tree): Adapt to generalize usage of _Base_ptr in place 
of _Link_type.
             * testsuite/23_containers/map/allocator/ext_ptr.cc: New 
test case.
             * testsuite/23_containers/multimap/allocator/ext_ptr.cc: 
New test case.
             * testsuite/23_containers/multiset/allocator/ext_ptr.cc: 
New test case.
             * testsuite/23_containers/set/allocator/ext_ptr.cc: New 
test case.

Ok to commit ?

François


On 09/10/2024 00:02, Jonathan Wakely wrote:
>
>
> On Tue, 8 Oct 2024 at 22:50, Jonathan Wakely <jwakely.gcc@gmail.com> 
> wrote:
>
>
>
>     On Thu, 1 Aug 2024 at 18:28, François Dumont
>     <frs.dumont@gmail.com> wrote:
>
>         Hi
>
>         Here is a proposal to add fancy pointer support in
>         std::_Rb_tree container.
>
>         As you'll see there are still several usages of
>         pointer_traits<>::pointer_to. The ones in _M_header_ptr() are
>         unavoidable.
>
>
>     Yes, those are necessary.
>
>     The pointer_to use in _M_const_cast could be simplified by adding
>     a _M_self_ptr() member to _Rb_tree_pnode_base:
>
>       _Base_ptr
>       _M_self_ptr() _GLIBCXX_NOEXCEPT
>       { return pointer_traits<_Base_ptr>::pointer_to(*this); }
>
>
> I don't think it's needed (because _M_const_cast is only in C++11 
> code, I think?) but you could define that for both C++11 and C++98:
>
> #if __cplusplus
>   _Base_ptr
>   _M_self_ptr() noexcept
>   { return pointer_traits<_Base_ptr>::pointer_to(*this); }
> #else
>   _Base_ptr _M_self_ptr() { return this; }
> #endif
>
> Then this works for both (if you replace the 'auto'):
>
>
>       _Base_ptr
>       _M_self_ptr_nc() const _GLIBCXX_NOEXCEPT
>       {
>         auto __self = const_cast<_Rb_tree_pnode_base*>(this);
>         return __self->_M_self_ptr();
>       }
>
>      _Const_Base_ptr
>       _M_self_ptr() const _GLIBCXX_NOEXCEPT
>       { return pointer_traits<_Const_Base_ptr>::pointer_to(*this); }
>
>     Then _M_const_cast would do:
>
>     return iterator(_M_node->_M_self_ptr_nc());
>
>         The ones to extract a node or to return a node to the
>         allocator are more questionable. Are they fine ? Is there
>         another way to
>         mimic the static_cast<_Link_type> that can be done on raw
>         pointers with
>         fancy pointers ?
>
>
>     I think you can just do
>     static_cast<_Link_type>(_M_node->_M_self_ptr()) i.e. convert the
>     _Base_ptr to the derived _Link_type
>     (we should really rename _Link_type to _Node_ptr, I keep getting
>     confused by the fact that the actual links between nodes are
>     _Base_ptr, so what does _Link_type mean now?!)
>
>     Alternatively, you could add a _M_node_ptr() to the
>     _Rb_tree_pnode_base type:
>
>     template<typename _NodeT>
>       __ptr_rebind<_BasePtr, _NodeT>
>       _M_node_ptr() _GLIBCXX_NOEXCEPT
>       {
>         auto __node = static_cast<_NodeT*>(this);
>         using _Node_ptr = __ptr_rebind<_BasePtr, _NodeT>;
>         return pointer_traits<_Node_ptr>::pointer_to(*__node);
>       }
>
>
> You could add a C++98 version of this as well:
>
> #if __cplusplus >= 201103L
>  // as above
> #else
>   template<typename _NodeT>
>   _NodeT*
>   _M_node_ptr() _GLIBCXX_NOEXCEPT
>   { return static_cast<_Node*>(this); }
> #endif
>
>
> Then the code to deallocate the node would not need to use #if:
>
> _Link_type __np = __p->template _M_node_ptr<_Node_type>();
> _Alloc_traits::deallocate(_M_get_Node_allocator(), __np, 1);
>
>
>
>
>     Then:
>       auto __np = __p->template _M_node_ptr<_Node_type>()
>     _Alloc_traits::deallocate(_M_get_Node_allocator(), __np, 1);
>
>
>     Some more general comments:
>
>     _Rb_tree_pnode_base and the iterators could just take one template
>     argument, and use __ptr_rebind to get the const pointer from the
>     non-const pointer. We can add a helper to do that:
>
>     template<typename _Ptr> struct __add_const_to_ptr;
>     #if __cplusplus >= 201103L
>     template<typename _Ptr>
>       using __add_const_to_ptr = __ptr_rebind<_Ptr, const typename
>     pointer_traits<_Ptr>::element_type>;
>     #else
>     template<typename _Ptr> struct __add_const_to_ptr;
>     template<typename _Tp>
>       struct __add_const_to_ptr<_Tp*>
>       { typedef const _Tp* __type; };
>     #endif
>
>     This will let you get the FooCPtr type from the FooPtr type.
>
>     A traits class could replace all the __conditional_t uses:
>
>     #if __cplusplus >= 201103L
>     template<typename _ValPtr>
>     struct _Rb_tree_node_traits
>     {
>       using _Base = _Rb_tree_pnode_base<_ValPtr>;
>       using _Node_type = _Rb_tree_pnode<_ValPtr>;
>       using _Base_ptr = __ptr_rebind<_ValPtr, _Base>;
>       using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
>       using iterator = _Rb_tree_piterator<_ValPtr>;
>       using const_iterator = _Rb_tree_const_piterator<_ValPtr>;
>     };
>     #else
>     template<typename _ValPtr>
>     struct _Rb_tree_node_traits;
>     #endif
>
>     template<typename _Val>
>     struct _Rb_tree_node_traits<_Val*>
>     {
>       typedef _Rb_tree_node_base _Base;
>       typedef _Rb_tree_node<_Val> _Node_type;
>       typedef _Base* _Base_ptr;
>       typedef _Node* _Node_ptr;
>       typedef _Rb_tree_iterator<_Val> iterator;
>       typedef _Rb_tree_const_iterator<_Val> const_iterator;
>     };
>
>     I think this would get rid of a lot of the #if checks and the
>     __conditional_t uses.
>
>     Otherwise this looks really good, nice work!
>
>
>         A solution to this (potential) issue would be to:
>
>         1. Store _Node_type pointer in the data structure rather than
>         _Node_base
>         pointer. But then _Rb_tree_pheader _M_left/_M_right could not be
>         initialized with a pointer to _M_parent which is only a
>         _Node_base
>         instance. We would then need to init it to nullptr but I
>         haven't check
>         impact on code and more importantly it would be an abi
>         breaking change.
>
>         2. Still store _Node_type pointer in the data structure but
>         then make
>         _M_header a _Node_type instance. In this case memory area of the
>         value_type would just be lost bytes.
>
>
>              libstdc++: Add fancy pointer support in std::[multi][map/set]
>
>
>     Let's just say "maps and sets" or "RB trees" instead of the more
>     cryptic std::[multi][map/set].
>
>
>              Support fancy allocator pointer type in std::_Rb_tree<>.
>
>              In case of fancy pointer type the container is now
>         storing the
>         pointer to
>              _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.
>
>              Many methods are adapted to take and return _Base_ptr in
>         place of
>         _Link_type.
>
>              libstdc++-v3/ChangeLog:
>
>                      * include/bits/stl_tree.h
>                      (_Rb_tree_pnode_base<>): New.
>                      (_Rb_tree_node_base): Inherit from latter.
>                      (_Rb_tree_pheader): New.
>                      (_Rb_tree_header): Inherit from latter.
>                      (_Rb_tree_node_val): New.
>                      (_Rb_tree_node): Inherit from latter.
>                      (_Rb_tree_pnode): New.
>                      (_Rb_tree_helpers): New.
>                      (_Rb_tree_piterator): New.
>                      (_Rb_tree_const_piterator): New.
>                      (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
>                      (_Rb_tree): Adapt to generalize usage of
>         _Base_ptr in place
>         of _Link_type.
>                      *
>         testsuite/23_containers/map/allocator/ext_ptr.cc: New
>         test case.
>                      *
>         testsuite/23_containers/multimap/allocator/ext_ptr.cc:
>         New test case.
>                      *
>         testsuite/23_containers/multiset/allocator/ext_ptr.cc:
>         New test case.
>                      *
>         testsuite/23_containers/set/allocator/ext_ptr.cc: New
>         test case.
>
>         Tested under Linux x64.
>
>         François
>
diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
index bc27e191e8b..6ea7aa54750 100644
--- a/libstdc++-v3/include/bits/stl_tree.h
+++ b/libstdc++-v3/include/bits/stl_tree.h
@@ -96,10 +96,97 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   enum _Rb_tree_color { _S_red = false, _S_black = true };
 
-  struct _Rb_tree_node_base
+#if __cplusplus >= 201103L
+  template<typename _Ptr>
+    struct __add_const_to_ptr
+    {
+      using __type =
+	__ptr_rebind<_Ptr, const typename pointer_traits<_Ptr>::element_type>;
+    };
+#else
+  template<typename _Ptr>
+    struct __add_const_to_ptr;
+#endif
+
+  template<typename _Tp>
+    struct __add_const_to_ptr<_Tp*>
+    { typedef const _Tp* __type; };
+
+#if __cplusplus >= 201103L
+  template<typename _Ptr>
+    struct _Rb_tree_ptr_traits
+    {
+      template<typename _Tp>
+	struct __rebind
+	{ using __type = __ptr_rebind<_Ptr, _Tp>; };
+
+      template<typename _BasePtr>
+	static _Ptr
+	_S_ptr_to(_BasePtr __this) noexcept
+	{ return pointer_traits<_Ptr>::pointer_to(*__this); }
+
+      template<typename _BasePtr, typename _UpPtr>
+	static _UpPtr
+	_S_up_ptr_to(_BasePtr __this) noexcept
+	{
+	  using __ptr_traits = pointer_traits<_UpPtr>;
+	  using __up_type = typename __ptr_traits::element_type;
+	  auto __up_this = static_cast<__up_type*>(__this);
+	  return __ptr_traits::pointer_to(*__up_this);
+	}
+    };
+#else
+  template<typename _Ptr>
+    struct _Rb_tree_ptr_traits;
+#endif
+
+  template<typename _Tp>
+    struct _Rb_tree_ptr_traits<_Tp*>
+    {
+      template<typename>
+	struct __rebind
+	{ typedef _Tp* __type; };
+
+      template<typename _BasePtr>
+	static _Tp*
+	_S_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
+	{ return static_cast<_Tp*>(__this); }
+
+      template<typename _BasePtr, typename _UpPtr>
+	static _UpPtr
+	_S_up_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
+	{ return static_cast<_UpPtr>(__this); }
+    };
+
+  template<typename _BasePtr>
+    struct _Rb_tree_pnode_base
+    {
+      typedef typename _Rb_tree_ptr_traits<_BasePtr>::template
+	__rebind<_Rb_tree_pnode_base>::__type _Base_ptr;
+      typedef typename __add_const_to_ptr<_Base_ptr>::__type _Const_Base_ptr;
+
+      _Base_ptr
+      _M_self_ptr() _GLIBCXX_NOEXCEPT
+      { return _Rb_tree_ptr_traits<_Base_ptr>::_S_ptr_to(this); }
+
+      _Const_Base_ptr
+      _M_self_ptr() const _GLIBCXX_NOEXCEPT
+      { return _Rb_tree_ptr_traits<_Const_Base_ptr>::_S_ptr_to(this); }
+
+      _Base_ptr
+      _M_self_ptr_nc() const _GLIBCXX_NOEXCEPT
+      {
+	_Rb_tree_pnode_base* __self = const_cast<_Rb_tree_pnode_base*>(this);
+	return __self->_M_self_ptr();
+      }
+
+      template<typename _NodePtr>
+	_NodePtr
+	_M_node_ptr() _GLIBCXX_NOEXCEPT
 	{
-    typedef _Rb_tree_node_base* _Base_ptr;
-    typedef const _Rb_tree_node_base* _Const_Base_ptr;
+	  return _Rb_tree_ptr_traits<_Base_ptr>::template
+	    _S_up_ptr_to<_Rb_tree_pnode_base*, _NodePtr>(this);
+	}
 
       _Rb_tree_color	_M_color;
       _Base_ptr		_M_parent;
@@ -109,32 +196,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       static _Base_ptr
       _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
       {
-      while (__x->_M_left != 0) __x = __x->_M_left;
+	while (__x->_M_left) __x = __x->_M_left;
 	return __x;
       }
 
       static _Const_Base_ptr
       _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
       {
-      while (__x->_M_left != 0) __x = __x->_M_left;
+	while (__x->_M_left) __x = __x->_M_left;
 	return __x;
       }
 
       static _Base_ptr
       _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
       {
-      while (__x->_M_right != 0) __x = __x->_M_right;
+	while (__x->_M_right) __x = __x->_M_right;
 	return __x;
       }
 
       static _Const_Base_ptr
       _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
       {
-      while (__x->_M_right != 0) __x = __x->_M_right;
+	while (__x->_M_right) __x = __x->_M_right;
 	return __x;
       }
     };
 
+  struct _Rb_tree_node_base
+    : _Rb_tree_pnode_base<_Rb_tree_node_base*>
+  { };
+
   // Helper type offering value initialization guarantee on the compare functor.
   template<typename _Key_compare>
     struct _Rb_tree_key_compare
@@ -163,21 +254,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   // Helper type to manage default initialization of node count and header.
-  struct _Rb_tree_header
+  template<typename _NodeBase>
+    struct _Rb_tree_pheader
     {
-    _Rb_tree_node_base	_M_header;
+    private:
+      typedef typename _NodeBase::_Base_ptr		_Base_ptr;
+      typedef typename _NodeBase::_Const_Base_ptr	_Const_Base_ptr;
+
+    public:
+      _NodeBase		_M_header;
       size_t		_M_node_count; // Keeps track of size of tree.
 
-    _Rb_tree_header() _GLIBCXX_NOEXCEPT
+      _Rb_tree_pheader() _GLIBCXX_NOEXCEPT
       {
 	_M_header._M_color = _S_red;
 	_M_reset();
       }
 
 #if __cplusplus >= 201103L
-    _Rb_tree_header(_Rb_tree_header&& __x) noexcept
+      _Rb_tree_pheader(_Rb_tree_pheader&& __x) noexcept
       {
-      if (__x._M_header._M_parent != nullptr)
+	if (__x._M_header._M_parent)
 	  _M_move_data(__x);
 	else
 	  {
@@ -188,13 +285,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
       void
-    _M_move_data(_Rb_tree_header& __from)
+      _M_move_data(_Rb_tree_pheader& __from)
       {
 	_M_header._M_color = __from._M_header._M_color;
 	_M_header._M_parent = __from._M_header._M_parent;
 	_M_header._M_left = __from._M_header._M_left;
 	_M_header._M_right = __from._M_header._M_right;
-      _M_header._M_parent->_M_parent = &_M_header;
+	_M_header._M_parent->_M_parent = _M_header._M_self_ptr();
 	_M_node_count = __from._M_node_count;
 
 	__from._M_reset();
@@ -203,41 +300,65 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       void
       _M_reset()
       {
-      _M_header._M_parent = 0;
-      _M_header._M_left = &_M_header;
-      _M_header._M_right = &_M_header;
+	_M_header._M_parent = _Base_ptr();
+	_M_header._M_left = _M_header._M_right = _M_header._M_self_ptr();
 	_M_node_count = 0;
       }
     };
 
+  struct _Rb_tree_header : _Rb_tree_pheader<_Rb_tree_node_base>
+  { };
+
   template<typename _Val>
-    struct _Rb_tree_node : public _Rb_tree_node_base
+    struct _Rb_tree_node_val
     {
-      typedef _Rb_tree_node<_Val>* _Link_type;
-
 #if __cplusplus < 201103L
       _Val _M_value_field;
 
+      __attribute__((__always_inline__))
       _Val*
       _M_valptr()
       { return std::__addressof(_M_value_field); }
 
+      __attribute__((__always_inline__))
       const _Val*
       _M_valptr() const
       { return std::__addressof(_M_value_field); }
 #else
       __gnu_cxx::__aligned_membuf<_Val> _M_storage;
 
+      [[__gnu__::__always_inline__]]
       _Val*
       _M_valptr()
       { return _M_storage._M_ptr(); }
 
+      [[__gnu__::__always_inline__]]
       const _Val*
       _M_valptr() const
       { return _M_storage._M_ptr(); }
 #endif
     };
 
+  template<typename _Val>
+    struct _Rb_tree_node
+    : _Rb_tree_node_base
+    , _Rb_tree_node_val<_Val>
+    {
+      typedef _Rb_tree_node<_Val>* _Node_ptr;
+      typedef const _Rb_tree_node<_Val>* _Const_Node_ptr;
+    };
+
+#if __cplusplus >= 201103L
+  template<typename _ValPtr>
+    struct _Rb_tree_pnode
+    : _Rb_tree_pnode_base<_ValPtr>
+    , _Rb_tree_node_val<typename std::pointer_traits<_ValPtr>::element_type>
+    {
+      using _Node_ptr = __ptr_rebind<_ValPtr, _Rb_tree_pnode>;
+      using _Const_Node_ptr = typename __add_const_to_ptr<_Node_ptr>::__type;
+    };
+#endif
+
   _GLIBCXX_PURE _Rb_tree_node_base*
   _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
 
@@ -250,6 +371,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _GLIBCXX_PURE const _Rb_tree_node_base*
   _Rb_tree_decrement(const _Rb_tree_node_base* __x) throw ();
 
+  __attribute__((__nonnull__))
+  void
+  _Rb_tree_insert_and_rebalance(const bool __insert_left,
+				_Rb_tree_node_base* __x,
+				_Rb_tree_node_base* __p,
+				_Rb_tree_node_base& __header) throw ();
+
+  __attribute__((__nonnull__,__returns_nonnull__))
+  _Rb_tree_node_base*
+  _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
+			       _Rb_tree_node_base& __header) throw ();
+
   template<typename _Tp>
     struct _Rb_tree_iterator
     {
@@ -261,8 +394,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef ptrdiff_t				difference_type;
 
       typedef _Rb_tree_iterator<_Tp>			_Self;
-      typedef _Rb_tree_node_base::_Base_ptr	_Base_ptr;
-      typedef _Rb_tree_node<_Tp>*		_Link_type;
+      typedef typename _Rb_tree_node_base::_Base_ptr	_Base_ptr;
+      typedef _Rb_tree_node<_Tp>*			_Node_ptr;
 
       _Rb_tree_iterator() _GLIBCXX_NOEXCEPT
       : _M_node() { }
@@ -273,11 +406,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       reference
       operator*() const _GLIBCXX_NOEXCEPT
-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type> (_M_node)->_M_valptr(); }
+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       _Self&
       operator++() _GLIBCXX_NOEXCEPT
@@ -335,8 +468,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef ptrdiff_t			 difference_type;
 
       typedef _Rb_tree_const_iterator<_Tp>			_Self;
-      typedef _Rb_tree_node_base::_Const_Base_ptr	_Base_ptr;
-      typedef const _Rb_tree_node<_Tp>*			_Link_type;
+      typedef typename _Rb_tree_node_base::_Const_Base_ptr	_Base_ptr;
+      typedef const _Rb_tree_node<_Tp>*				_Node_ptr;
 
       _Rb_tree_const_iterator() _GLIBCXX_NOEXCEPT
       : _M_node() { }
@@ -350,15 +483,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       _M_const_cast() const _GLIBCXX_NOEXCEPT
-      { return iterator(const_cast<typename iterator::_Base_ptr>(_M_node)); }
+      { return iterator(_M_node->_M_self_ptr_nc()); }
 
       reference
       operator*() const _GLIBCXX_NOEXCEPT
-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       _Self&
       operator++() _GLIBCXX_NOEXCEPT
@@ -403,17 +536,641 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Base_ptr _M_node;
     };
 
+#if __cplusplus >= 201103L
+  template<typename _NodeBase>
+    struct _Rb_tree_helpers
+    {
+      using _Base_ptr = typename _NodeBase::_Base_ptr;
+      using _Const_Base_ptr = typename _NodeBase::_Const_Base_ptr;
+
+      static _Base_ptr
+      _S_minimum(_Base_ptr __x)
+      { return _NodeBase::_S_minimum(__x); }
+
+      static _Const_Base_ptr
+      _S_minimum(_Const_Base_ptr __x)
+      { return _NodeBase::_S_minimum(__x); }
+
+      static _Base_ptr
+      _S_maximum(_Base_ptr __x)
+      { return _NodeBase::_S_maximum(__x); }
+
+      static _Const_Base_ptr
+      _S_maximum(_Const_Base_ptr __x)
+      { return _NodeBase::_S_maximum(__x); }
+
+      static _Base_ptr
+      _Increment(_Base_ptr __x) noexcept
+      {
+	if (__x->_M_right)
+	  {
+	    __x = __x->_M_right;
+	    while (__x->_M_left)
+	      __x = __x->_M_left;
+	  }
+	else
+	  {
+	    _Base_ptr __y = __x->_M_parent;
+	    while (__x == __y->_M_right)
+	      {
+		__x = __y;
+		__y = __y->_M_parent;
+	      }
+	    if (__x->_M_right != __y)
+	      __x = __y;
+	  }
+	return __x;
+      }
+
+      static _Const_Base_ptr
+      _Increment(_Const_Base_ptr __x) noexcept
+      {
+	if (__x->_M_right)
+	  {
+	    __x = __x->_M_right;
+	    while (__x->_M_left)
+	      __x = __x->_M_left;
+	  }
+	else
+	  {
+	    _Const_Base_ptr __y = __x->_M_parent;
+	    while (__x == __y->_M_right)
+	      {
+		__x = __y;
+		__y = __y->_M_parent;
+	      }
+	    if (__x->_M_right != __y)
+	      __x = __y;
+	  }
+	return __x;
+      }
+
+      static _Base_ptr
+      _Decrement(_Base_ptr __x) noexcept
+      {
+	if (__x->_M_color == _S_red
+	    && __x->_M_parent->_M_parent == __x)
+	  __x = __x->_M_right;
+	else if (__x->_M_left)
+	  {
+	    _Base_ptr __y = __x->_M_left;
+	    while (__y->_M_right)
+	      __y = __y->_M_right;
+	    __x = __y;
+	  }
+	else
+	  {
+	    _Base_ptr __y = __x->_M_parent;
+	    while (__x == __y->_M_left)
+	      {
+		__x = __y;
+		__y = __y->_M_parent;
+	      }
+	    __x = __y;
+	  }
+	return __x;
+      }
+
+      static _Const_Base_ptr
+      _Decrement(_Const_Base_ptr __x) noexcept
+      {
+	if (__x->_M_color == _S_red
+	    && __x->_M_parent->_M_parent == __x)
+	  __x = __x->_M_right;
+	else if (__x->_M_left)
+	  {
+	    _Const_Base_ptr __y = __x->_M_left;
+	    while (__y->_M_right)
+	      __y = __y->_M_right;
+	    __x = __y;
+	  }
+	else
+	  {
+	    _Const_Base_ptr __y = __x->_M_parent;
+	    while (__x == __y->_M_left)
+	      {
+		__x = __y;
+		__y = __y->_M_parent;
+	      }
+	    __x = __y;
+	  }
+	return __x;
+      }
+
+      static void
+      _Rotate_left(_Base_ptr __x, _Base_ptr& __root)
+      {
+	const _Base_ptr __y = __x->_M_right;
+
+	__x->_M_right = __y->_M_left;
+	if (__y->_M_left)
+	  __y->_M_left->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_left)
+	  __x->_M_parent->_M_left = __y;
+	else
+	  __x->_M_parent->_M_right = __y;
+	__y->_M_left = __x;
+	__x->_M_parent = __y;
+      }
+
+      static void
+      _Rotate_right(_Base_ptr __x, _Base_ptr& __root)
+      {
+	const _Base_ptr __y = __x->_M_left;
+
+	__x->_M_left = __y->_M_right;
+	if (__y->_M_right)
+	  __y->_M_right->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_right)
+	  __x->_M_parent->_M_right = __y;
+	else
+	  __x->_M_parent->_M_left = __y;
+	__y->_M_right = __x;
+	__x->_M_parent = __y;
+      }
+
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _Base_ptr __x, _Base_ptr __p,
+			      _NodeBase& __header)
+      {
+	_Base_ptr& __root = __header._M_parent;
+
+	// Initialize fields in new node to insert.
+	__x->_M_parent = __p;
+	__x->_M_left = __x->_M_right = nullptr;
+	__x->_M_color = _S_red;
+
+	// Insert.
+	// Make new node child of parent and maintain root, leftmost and
+	// rightmost nodes.
+	// N.B. First node is always inserted left.
+	if (__insert_left)
+	  {
+	    __p->_M_left = __x; // also makes leftmost = __x when __p == &__header
+
+	    if (std::__to_address(__p) == std::addressof(__header))
+	      {
+		__header._M_parent = __x;
+		__header._M_right = __x;
+	      }
+	    else if (__p == __header._M_left)
+	      __header._M_left = __x; // maintain leftmost pointing to min node
+	  }
+	else
+	  {
+	    __p->_M_right = __x;
+
+	    if (__p == __header._M_right)
+	      __header._M_right = __x; // maintain rightmost pointing to max node
+	  }
+	// Rebalance.
+	while (__x != __root
+	       && __x->_M_parent->_M_color == _S_red)
+	  {
+	    const _Base_ptr __xpp = __x->_M_parent->_M_parent;
+
+	    if (__x->_M_parent == __xpp->_M_left)
+	      {
+		const _Base_ptr __y = __xpp->_M_right;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_right)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_left(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_right(__xpp, __root);
+		  }
+	      }
+	    else
+	      {
+		const _Base_ptr __y = __xpp->_M_left;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_left)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_right(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_left(__xpp, __root);
+		  }
+	      }
+	  }
+	__root->_M_color = _S_black;
+      }
+
+      static _Base_ptr
+      _S_rebalance_for_erase(_Base_ptr __z, _NodeBase& __header)
+      {
+	_Base_ptr& __root = __header._M_parent;
+	_Base_ptr& __leftmost = __header._M_left;
+	_Base_ptr& __rightmost = __header._M_right;
+	_Base_ptr __y = __z;
+	_Base_ptr __x{};
+	_Base_ptr __x_parent{};
+
+	if (!__y->_M_left)     // __z has at most one non-null child. y == z.
+	  __x = __y->_M_right;     // __x might be null.
+	else
+	  if (!__y->_M_right)  // __z has exactly one non-null child. y == z.
+	    __x = __y->_M_left;    // __x is not null.
+	  else
+	    {
+	      // __z has two non-null children.  Set __y to
+	      __y = __y->_M_right;   //   __z's successor.  __x might be null.
+	      while (__y->_M_left)
+		__y = __y->_M_left;
+	      __x = __y->_M_right;
+	    }
+	if (__y != __z)
+	  {
+	    // relink y in place of z.  y is z's successor
+	    __z->_M_left->_M_parent = __y;
+	    __y->_M_left = __z->_M_left;
+	    if (__y != __z->_M_right)
+	      {
+		__x_parent = __y->_M_parent;
+		if (__x)
+		  __x->_M_parent = __y->_M_parent;
+		__y->_M_parent->_M_left = __x;   // __y must be a child of _M_left
+		__y->_M_right = __z->_M_right;
+		__z->_M_right->_M_parent = __y;
+	      }
+	    else
+	      __x_parent = __y;
+	    if (__root == __z)
+	      __root = __y;
+	    else if (__z->_M_parent->_M_left == __z)
+	      __z->_M_parent->_M_left = __y;
+	    else
+	      __z->_M_parent->_M_right = __y;
+	    __y->_M_parent = __z->_M_parent;
+	    std::swap(__y->_M_color, __z->_M_color);
+	    __y = __z;
+	    // __y now points to node to be actually deleted
+	  }
+	else
+	  {                        // __y == __z
+	    __x_parent = __y->_M_parent;
+	    if (__x)
+	      __x->_M_parent = __y->_M_parent;
+	    if (__root == __z)
+	      __root = __x;
+	    else
+	      if (__z->_M_parent->_M_left == __z)
+		__z->_M_parent->_M_left = __x;
+	      else
+		__z->_M_parent->_M_right = __x;
+	    if (__leftmost == __z)
+	      {
+		if (!__z->_M_right)        // __z->_M_left must be null also
+		  __leftmost = __z->_M_parent;
+		// makes __leftmost == _M_header if __z == __root
+		else
+		  __leftmost = _S_minimum(__x);
+	      }
+	    if (__rightmost == __z)
+	      {
+		if (__z->_M_left == 0)         // __z->_M_right must be null also
+		  __rightmost = __z->_M_parent;
+		// makes __rightmost == _M_header if __z == __root
+		else                      // __x == __z->_M_left
+		  __rightmost = _S_maximum(__x);
+	      }
+	  }
+	if (__y->_M_color != _S_red)
+	  {
+	    while (__x != __root && (__x == 0 || __x->_M_color == _S_black))
+	      if (__x == __x_parent->_M_left)
+		{
+		  _Base_ptr __w = __x_parent->_M_right;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_left(__x_parent, __root);
+		      __w = __x_parent->_M_right;
+		    }
+		  if ((!__w->_M_left || __w->_M_left->_M_color == _S_black) &&
+		      (!__w->_M_right || __w->_M_right->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_right || __w->_M_right->_M_color == _S_black)
+			{
+			  __w->_M_left->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_right(__w, __root);
+			  __w = __x_parent->_M_right;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_right)
+			__w->_M_right->_M_color = _S_black;
+		      _Rotate_left(__x_parent, __root);
+		      break;
+		    }
+		}
+	      else
+		{
+		  // same as above, with _M_right <-> _M_left.
+		  _Base_ptr __w = __x_parent->_M_left;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_right(__x_parent, __root);
+		      __w = __x_parent->_M_left;
+		    }
+		  if ((!__w->_M_right || __w->_M_right->_M_color == _S_black) &&
+		      (!__w->_M_left || __w->_M_left->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_left || __w->_M_left->_M_color == _S_black)
+			{
+			  __w->_M_right->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_left(__w, __root);
+			  __w = __x_parent->_M_left;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_left)
+			__w->_M_left->_M_color = _S_black;
+		      _Rotate_right(__x_parent, __root);
+		      break;
+		    }
+		}
+	    if (__x)
+	      __x->_M_color = _S_black;
+	  }
+
+	return __y;
+      }
+  };
+#else
+  template<typename _NodeBase>
+    struct _Rb_tree_helpers;
+#endif
+
+  template<>
+    struct _Rb_tree_helpers<_Rb_tree_node_base>
+    {
+      static _Rb_tree_node_base*
+      _S_minimum(_Rb_tree_node_base* __x) _GLIBCXX_NOEXCEPT
+      { return _Rb_tree_node_base::_S_minimum(__x); }
+
+      static const _Rb_tree_node_base*
+      _S_minimum(const _Rb_tree_node_base* __x) _GLIBCXX_NOEXCEPT
+      { return _Rb_tree_node_base::_S_minimum(__x); }
+
+      static _Rb_tree_node_base*
+      _S_maximum(_Rb_tree_node_base* __x) _GLIBCXX_NOEXCEPT
+      { return _Rb_tree_node_base::_S_maximum(__x); }
+
+      static const _Rb_tree_node_base*
+      _S_maximum(const _Rb_tree_node_base* __x) _GLIBCXX_NOEXCEPT
+      { return _Rb_tree_node_base::_S_maximum(__x); }
+
       __attribute__((__nonnull__))
-  void
-  _Rb_tree_insert_and_rebalance(const bool __insert_left,
-				_Rb_tree_node_base* __x,
-				_Rb_tree_node_base* __p,
-				_Rb_tree_node_base& __header) throw ();
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _Rb_tree_node_base* __x, _Rb_tree_node_base* __p,
+			      _Rb_tree_node_base& __header) throw ()
+      {
+	return _Rb_tree_insert_and_rebalance(__insert_left, __x, __p, __header);
+      }
 
-  __attribute__((__nonnull__,__returns_nonnull__))
-  _Rb_tree_node_base*
-  _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
-			       _Rb_tree_node_base& __header) throw ();
+      __attribute__((__nonnull__))
+      static _Rb_tree_node_base*
+      _S_rebalance_for_erase(_Rb_tree_node_base* const __z,
+			     _Rb_tree_node_base& __header) throw ()
+      { return _Rb_tree_rebalance_for_erase(__z, __header); }
+    };
+
+#if __cplusplus >= 201103L
+  template<typename _ValPtr>
+    struct _Rb_tree_piterator
+    {
+      using __ptr_traits = pointer_traits<_ValPtr>;
+      using value_type = typename __ptr_traits::element_type;
+      using reference = value_type&;
+      using pointer = value_type*;
+
+      typedef bidirectional_iterator_tag iterator_category;
+      typedef ptrdiff_t			 difference_type;
+
+      typedef _Rb_tree_piterator			_Self;
+      typedef _Rb_tree_pnode_base<_ValPtr>		_Node_base;
+      typedef _Rb_tree_pnode<_ValPtr>			_Node_type;
+      typedef typename _Node_type::_Base_ptr		_Base_ptr;
+      typedef typename _Node_type::_Node_ptr		_Node_ptr;
+      typedef _Rb_tree_helpers<_Node_base>		_Tree_helpers;
+
+      _Rb_tree_piterator() _GLIBCXX_NOEXCEPT
+      : _M_node() { }
+
+      explicit
+      _Rb_tree_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      : _M_node(__x) { }
+
+      reference
+      operator*() const _GLIBCXX_NOEXCEPT
+      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
+
+      pointer
+      operator->() const _GLIBCXX_NOEXCEPT
+      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
+
+      _Self&
+      operator++() _GLIBCXX_NOEXCEPT
+      {
+	_M_node = _Tree_helpers::_Increment(_M_node);
+	return *this;
+      }
+
+      _Self
+      operator++(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	_M_node = _Tree_helpers::_Increment(_M_node);
+	return __tmp;
+      }
+
+      _Self&
+      operator--() _GLIBCXX_NOEXCEPT
+      {
+	_M_node = _Tree_helpers::_Decrement(_M_node);
+	return *this;
+      }
+
+      _Self
+      operator--(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	_M_node = _Tree_helpers::_Decrement(_M_node);
+	return __tmp;
+      }
+
+      friend bool
+      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node == __y._M_node; }
+
+#if ! __cpp_lib_three_way_comparison
+      friend bool
+      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node != __y._M_node; }
+#endif
+
+      _Base_ptr _M_node;
+    };
+
+  template<typename _ValPtr>
+    struct _Rb_tree_const_piterator
+    {
+      using __ptr_traits = pointer_traits<_ValPtr>;
+      using value_type = typename __ptr_traits::element_type;
+      using reference = const value_type&;
+      using pointer = const value_type*;
+
+      typedef _Rb_tree_piterator<_ValPtr>		iterator;
+      typedef typename iterator::_Base_ptr		_Base_ptr;
+
+      typedef bidirectional_iterator_tag iterator_category;
+      typedef ptrdiff_t			 difference_type;
+
+      typedef _Rb_tree_const_piterator			_Self;
+      typedef _Rb_tree_pnode_base<_ValPtr>		_Node_base;
+      typedef _Rb_tree_pnode<_ValPtr>			_Node_type;
+      typedef typename _Node_type::_Const_Base_ptr	_Const_Base_ptr;
+      typedef _Rb_tree_helpers<_Node_base>		_Tree_helpers;
+
+      _Rb_tree_const_piterator() _GLIBCXX_NOEXCEPT
+      : _M_node() { }
+
+      explicit
+      _Rb_tree_const_piterator(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      : _M_node(__x) { }
+
+      _Rb_tree_const_piterator(const iterator& __it) _GLIBCXX_NOEXCEPT
+      : _M_node(__it._M_node) { }
+
+      iterator
+      _M_const_cast() const _GLIBCXX_NOEXCEPT
+      { return iterator(_M_node->_M_self_ptr_nc()); }
+
+      reference
+      operator*() const _GLIBCXX_NOEXCEPT
+      { return *static_cast<const _Node_type&>(*_M_node)._M_valptr(); }
+
+      pointer
+      operator->() const _GLIBCXX_NOEXCEPT
+      { return static_cast<const _Node_type&>(*_M_node)._M_valptr(); }
+
+      _Self&
+      operator++() _GLIBCXX_NOEXCEPT
+      {
+	_M_node = _Tree_helpers::_Increment(_M_node);
+	return *this;
+      }
+
+      _Self
+      operator++(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	_M_node = _Tree_helpers::_Increment(_M_node);
+	return __tmp;
+      }
+
+      _Self&
+      operator--() _GLIBCXX_NOEXCEPT
+      {
+	_M_node = _Tree_helpers::_Decrement(_M_node);
+	return *this;
+      }
+
+      _Self
+      operator--(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	_M_node = _Tree_helpers::_Decrement(_M_node);
+	return __tmp;
+      }
+
+      friend bool
+      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node == __y._M_node; }
+
+#if ! __cpp_lib_three_way_comparison
+      friend bool
+      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node != __y._M_node; }
+#endif
+
+      _Const_Base_ptr _M_node;
+    };
+#endif // C++11
+
+#if __cplusplus >= 201103L
+  template<typename _ValPtr>
+    struct _Rb_tree_node_traits
+    {
+      using _Node_base		= _Rb_tree_pnode_base<_ValPtr>;
+      using _Node_type		= _Rb_tree_pnode<_ValPtr>;
+      using _Header_t		= _Rb_tree_pheader<_Node_base>;
+      using _Iterator_t		= _Rb_tree_piterator<_ValPtr>;
+      using _Const_iterator_t	= _Rb_tree_const_piterator<_ValPtr>;
+    };
+#else
+  template<typename _ValPtr>
+    struct _Rb_tree_node_traits;
+#endif
+
+  template<typename _Val>
+    struct _Rb_tree_node_traits<_Val*>
+    {
+      typedef _Rb_tree_node_base		_Node_base;
+      typedef _Rb_tree_node<_Val>		_Node_type;
+      typedef _Rb_tree_header			_Header_t;
+      typedef _Rb_tree_iterator<_Val>		_Iterator_t;
+      typedef _Rb_tree_const_iterator<_Val>	_Const_iterator_t;
+    };
 
 #if __cplusplus > 201402L
   template<typename _Tree1, typename _Cmp2>
@@ -424,16 +1181,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc = allocator<_Val> >
     class _Rb_tree
     {
+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::pointer _ValPtr;
+      typedef _Rb_tree_node_traits<_ValPtr> _Node_traits;
+
+      typedef typename _Node_traits::_Node_base		_Node_base;
+      typedef typename _Node_traits::_Node_type		_Node_type;
+
       typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
-	rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
+	rebind<_Node_type>::other _Node_allocator;
 
       typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
 
     protected:
-      typedef _Rb_tree_node_base* 		_Base_ptr;
-      typedef const _Rb_tree_node_base* 	_Const_Base_ptr;
-      typedef _Rb_tree_node<_Val>* 		_Link_type;
-      typedef const _Rb_tree_node<_Val>*	_Const_Link_type;
+      typedef typename _Node_base::_Base_ptr 		_Base_ptr;
+      typedef typename _Node_base::_Const_Base_ptr 	_Const_Base_ptr;
+      typedef typename _Node_type::_Node_ptr 		_Node_ptr;
+      typedef typename _Node_type::_Const_Node_ptr	_Const_Node_ptr;
+      typedef _Rb_tree_helpers<_Node_base>		_Tree_helpers;
 
     private:
       // Functor recycling a pool of nodes and using allocation once the pool
@@ -459,13 +1223,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
 	~_Reuse_or_alloc_node()
-	{ _M_t._M_erase(static_cast<_Link_type>(_M_root)); }
+	{ _M_t._M_erase(_M_root); }
 
 	template<typename _Arg>
-	  _Link_type
+	  _Base_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg)
 	  {
-	    _Link_type __node = static_cast<_Link_type>(_M_extract());
+	    _Base_ptr __node = _M_extract();
 	    if (__node)
 	      {
 		_M_t._M_destroy_node(__node);
@@ -524,7 +1288,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: _M_t(__t) { }
 
 	template<typename _Arg>
-	  _Link_type
+	  _Node_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg) const
 	  { return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg)); }
 
@@ -556,18 +1320,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return allocator_type(_M_get_Node_allocator()); }
 
     protected:
-      _Link_type
+      _Node_ptr
       _M_get_node()
       { return _Alloc_traits::allocate(_M_get_Node_allocator(), 1); }
 
       void
-      _M_put_node(_Link_type __p) _GLIBCXX_NOEXCEPT
-      { _Alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1); }
+      _M_put_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
+      {
+	_Alloc_traits::deallocate
+	  (_M_get_Node_allocator(), __p->template _M_node_ptr<_Node_ptr>(), 1);
+      }
 
 #if __cplusplus < 201103L
       void
-      _M_construct_node(_Link_type __node, const value_type& __x)
+      _M_construct_node(_Base_ptr __p, const value_type& __x)
       {
+	_Node_ptr __node = static_cast<_Node_ptr>(__p);
 	__try
 	  { get_allocator().construct(__node->_M_valptr(), __x); }
 	__catch(...)
@@ -577,79 +1345,82 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  }
       }
 
-      _Link_type
+      _Node_ptr
       _M_create_node(const value_type& __x)
       {
-	_Link_type __tmp = _M_get_node();
+	_Node_ptr __tmp = _M_get_node();
 	_M_construct_node(__tmp, __x);
 	return __tmp;
       }
 #else
       template<typename... _Args>
 	void
-	_M_construct_node(_Link_type __node, _Args&&... __args)
+	_M_construct_node(_Base_ptr __p, _Args&&... __args)
 	{
+	  _Node_type& __node = static_cast<_Node_type&>(*__p);
 	  __try
 	    {
-	      ::new(__node) _Rb_tree_node<_Val>;
+	      ::new(std::__addressof(__node)) _Node_type;
 	      _Alloc_traits::construct(_M_get_Node_allocator(),
-				       __node->_M_valptr(),
+				       __node._M_valptr(),
 				       std::forward<_Args>(__args)...);
 	    }
 	  __catch(...)
 	    {
-	      __node->~_Rb_tree_node<_Val>();
-	      _M_put_node(__node);
+	      __node.~_Node_type();
+	      _M_put_node(__p);
 	      __throw_exception_again;
 	    }
 	}
 
       template<typename... _Args>
-	_Link_type
+	_Node_ptr
 	_M_create_node(_Args&&... __args)
 	{
-	  _Link_type __tmp = _M_get_node();
+	  _Node_ptr __tmp = _M_get_node();
 	  _M_construct_node(__tmp, std::forward<_Args>(__args)...);
 	  return __tmp;
 	}
 #endif
 
       void
-      _M_destroy_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_destroy_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
       {
 #if __cplusplus < 201103L
-	get_allocator().destroy(__p->_M_valptr());
+	get_allocator().destroy(static_cast<_Node_ptr>(__p)->_M_valptr());
 #else
-	_Alloc_traits::destroy(_M_get_Node_allocator(), __p->_M_valptr());
-	__p->~_Rb_tree_node<_Val>();
+	_Node_type& __node = static_cast<_Node_type&>(*__p);
+	_Alloc_traits::destroy(_M_get_Node_allocator(), __node._M_valptr());
+	__node.~_Node_type();
 #endif
       }
 
       void
-      _M_drop_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_drop_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
       {
 	_M_destroy_node(__p);
 	_M_put_node(__p);
       }
 
       template<bool _MoveValue, typename _NodeGen>
-	_Link_type
-	_M_clone_node(_Link_type __x, _NodeGen& __node_gen)
+	_Base_ptr
+	_M_clone_node(_Base_ptr __x, _NodeGen& __node_gen)
 	{
 #if __cplusplus >= 201103L
 	  using _Vp = __conditional_t<_MoveValue,
 				      value_type&&,
 				      const value_type&>;
 #endif
-	  _Link_type __tmp
-	    = __node_gen(_GLIBCXX_FORWARD(_Vp, *__x->_M_valptr()));
+	  _Base_ptr __tmp = __node_gen
+	    (_GLIBCXX_FORWARD(_Vp, *static_cast<_Node_type&>(*__x)._M_valptr()));
 	  __tmp->_M_color = __x->_M_color;
-	  __tmp->_M_left = 0;
-	  __tmp->_M_right = 0;
+	  __tmp->_M_left = __tmp->_M_right = _Base_ptr();
 	  return __tmp;
 	}
 
     protected:
+      typedef typename _Node_traits::_Header_t		_Header_t;
+
 #if _GLIBCXX_INLINE_VERSION
       template<typename _Key_compare>
 #else
@@ -660,7 +1431,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	struct _Rb_tree_impl
 	: public _Node_allocator
 	, public _Rb_tree_key_compare<_Key_compare>
-	, public _Rb_tree_header
+	, public _Header_t
 	{
 	  typedef _Rb_tree_key_compare<_Key_compare> _Base_key_compare;
 
@@ -674,7 +1445,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Rb_tree_impl(const _Rb_tree_impl& __x)
 	  : _Node_allocator(_Alloc_traits::_S_select_on_copy(__x))
 	  , _Base_key_compare(__x._M_key_compare)
-	  , _Rb_tree_header()
+	  , _Header_t()
 	  { }
 
 #if __cplusplus < 201103L
@@ -694,7 +1465,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Rb_tree_impl(_Rb_tree_impl&& __x, _Node_allocator&& __a)
 	  : _Node_allocator(std::move(__a)),
 	    _Base_key_compare(std::move(__x)),
-	    _Rb_tree_header(std::move(__x))
+	    _Header_t(std::move(__x))
 	  { }
 
 	  _Rb_tree_impl(const _Key_compare& __comp, _Node_allocator&& __a)
@@ -730,31 +1501,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_rightmost() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_right; }
 
-      _Link_type
+      _Base_ptr
       _M_mbegin() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(this->_M_impl._M_header._M_parent); }
+      { return this->_M_impl._M_header._M_parent; }
 
-      _Link_type
+      _Base_ptr
       _M_begin() _GLIBCXX_NOEXCEPT
       { return _M_mbegin(); }
 
-      _Const_Link_type
+      _Const_Base_ptr
       _M_begin() const _GLIBCXX_NOEXCEPT
-      {
-	return static_cast<_Const_Link_type>
-	  (this->_M_impl._M_header._M_parent);
-      }
+      { return this->_M_impl._M_header._M_parent; }
 
       _Base_ptr
       _M_end() _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
+      { return this->_M_impl._M_header._M_self_ptr(); }
 
       _Const_Base_ptr
       _M_end() const _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
+      { return this->_M_impl._M_header._M_self_ptr(); }
 
       static const _Key&
-      _S_key(_Const_Link_type __x)
+      _S_key(_Const_Base_ptr __x)
       {
 #if __cplusplus >= 201103L
 	// If we're asking for the key we're presumably using the comparison
@@ -772,48 +1540,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 # endif // C++17
 #endif // C++11
 
-	return _KeyOfValue()(*__x->_M_valptr());
+	return _KeyOfValue()(*static_cast<const _Node_type&>(*__x)._M_valptr());
       }
 
-      static _Link_type
+      static _Base_ptr
       _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_left); }
+      { return __x->_M_left; }
 
-      static _Const_Link_type
+      static _Const_Base_ptr
       _S_left(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_left); }
+      { return __x->_M_left; }
 
-      static _Link_type
+      static _Base_ptr
       _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_right); }
+      { return __x->_M_right; }
 
-      static _Const_Link_type
+      static _Const_Base_ptr
       _S_right(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_right); }
-
-      static const _Key&
-      _S_key(_Const_Base_ptr __x)
-      { return _S_key(static_cast<_Const_Link_type>(__x)); }
+      { return __x->_M_right; }
 
       static _Base_ptr
       _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
+      { return _Tree_helpers::_S_minimum(__x); }
 
       static _Const_Base_ptr
       _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
+      { return _Tree_helpers::_S_minimum(__x); }
 
       static _Base_ptr
       _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
+      { return _Tree_helpers::_S_maximum(__x); }
 
       static _Const_Base_ptr
       _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
+      { return _Tree_helpers::_S_maximum(__x); }
 
     public:
-      typedef _Rb_tree_iterator<value_type>       iterator;
-      typedef _Rb_tree_const_iterator<value_type> const_iterator;
+      typedef typename _Node_traits::_Iterator_t	iterator;
+      typedef typename _Node_traits::_Const_iterator_t	const_iterator;
 
       typedef std::reverse_iterator<iterator>       reverse_iterator;
       typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
@@ -846,7 +1610,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_(_Base_ptr __x, _Base_ptr __y, _Arg&& __v, _NodeGen&);
 
       iterator
-      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Link_type __z);
+      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Base_ptr __z);
 
       template<typename _Arg>
 	iterator
@@ -857,10 +1621,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_equal_lower(_Arg&& __x);
 
       iterator
-      _M_insert_lower_node(_Base_ptr __p, _Link_type __z);
+      _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z);
 
       iterator
-      _M_insert_equal_lower_node(_Link_type __z);
+      _M_insert_equal_lower_node(_Base_ptr __z);
 #else
       template<typename _NodeGen>
 	iterator
@@ -879,22 +1643,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       enum { __as_lvalue, __as_rvalue };
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
-	_M_copy(_Link_type, _Base_ptr, _NodeGen&);
+	_Base_ptr
+	_M_copy(_Base_ptr, _Base_ptr, _NodeGen&);
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
+	_Base_ptr
 	_M_copy(const _Rb_tree& __x, _NodeGen& __gen)
 	{
-	  _Link_type __root =
+	  _Base_ptr __root =
 	    _M_copy<_MoveValues>(__x._M_mbegin(), _M_end(), __gen);
-	  _M_leftmost() = _S_minimum(__root);
-	  _M_rightmost() = _S_maximum(__root);
+	  _M_leftmost() = _Tree_helpers::_S_minimum(__root);
+	  _M_rightmost() = _Tree_helpers::_S_maximum(__root);
 	  _M_impl._M_node_count = __x._M_impl._M_node_count;
 	  return __root;
 	}
 
-      _Link_type
+      _Base_ptr
       _M_copy(const _Rb_tree& __x)
       {
 	_Alloc_node __an(*this);
@@ -902,22 +1666,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       void
-      _M_erase(_Link_type __x);
+      _M_erase(_Base_ptr __x);
 
       iterator
-      _M_lower_bound(_Link_type __x, _Base_ptr __y,
+      _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k);
 
       const_iterator
-      _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _M_lower_bound(_Const_Base_ptr __x, _Const_Base_ptr __y,
 		     const _Key& __k) const;
 
       iterator
-      _M_upper_bound(_Link_type __x, _Base_ptr __y,
+      _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k);
 
       const_iterator
-      _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _M_upper_bound(_Const_Base_ptr __x, _Const_Base_ptr __y,
 		     const _Key& __k) const;
 
     public:
@@ -935,7 +1699,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x)
       : _M_impl(__x._M_impl)
       {
-	if (__x._M_root() != 0)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -947,7 +1711,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x, const allocator_type& __a)
       : _M_impl(__x._M_impl._M_key_compare, _Node_allocator(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -966,7 +1730,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a, false_type)
       : _M_impl(__x._M_impl._M_key_compare, std::move(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_move_data(__x, false_type{});
       }
 
@@ -1001,11 +1765,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       end() _GLIBCXX_NOEXCEPT
-      { return iterator(&this->_M_impl._M_header); }
+      { return iterator(this->_M_impl._M_header._M_self_ptr()); }
 
       const_iterator
       end() const _GLIBCXX_NOEXCEPT
-      { return const_iterator(&this->_M_impl._M_header); }
+      { return const_iterator(this->_M_impl._M_header._M_self_ptr()); }
 
       reverse_iterator
       rbegin() _GLIBCXX_NOEXCEPT
@@ -1530,10 +2294,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       node_type
       extract(const_iterator __pos)
       {
-	auto __ptr = _Rb_tree_rebalance_for_erase(
-	    __pos._M_const_cast()._M_node, _M_impl._M_header);
+	auto __ptr = _Tree_helpers::_S_rebalance_for_erase
+	  (__pos._M_const_cast()._M_node, _M_impl._M_header);
 	--_M_impl._M_node_count;
-	return { static_cast<_Link_type>(__ptr), _M_get_Node_allocator() };
+	return
+	  { __ptr->template _M_node_ptr<_Node_ptr>(), _M_get_Node_allocator() };
       }
 
       /// Extract a node.
@@ -1567,11 +2332,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr = _Tree_helpers::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
-		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+		  _M_insert_node(__res.first, __res.second, __ptr);
 		}
 	    }
 	}
@@ -1589,11 +2353,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr = _Tree_helpers::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
-		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+		  _M_insert_node(__res.first, __res.second, __ptr);
 		}
 	    }
 	}
@@ -1666,7 +2429,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
 	_Rb_tree& _M_t;
-	_Link_type _M_node;
+	_Node_ptr _M_node;
       };
 #endif // C++11
     };
@@ -1704,7 +2467,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_move_assign(_Rb_tree& __x, true_type)
     {
       clear();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	_M_move_data(__x, true_type());
       std::__alloc_on_move(_M_get_Node_allocator(),
 			   __x._M_get_Node_allocator());
@@ -1723,7 +2486,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // structure.
       _Reuse_or_alloc_node __roan(*this);
       _M_impl._M_reset();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	{
 	  _M_root() = _M_copy<__as_rvalue>(__x, __roan);
 	  __x.clear();
@@ -1798,7 +2561,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Reuse_or_alloc_node __roan(*this);
 	  _M_impl._M_reset();
 	  _M_impl._M_key_compare = __x._M_impl._M_key_compare;
-	  if (__x._M_root() != 0)
+	  if (__x._M_root())
 	    _M_root() = _M_copy<__as_lvalue>(__x, __roan);
 	}
 
@@ -1826,10 +2589,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 			      || _M_impl._M_key_compare(_KeyOfValue()(__v),
 							_S_key(__p)));
 
-	_Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
+	_Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
 
-	_Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				      this->_M_impl._M_header);
+	_Tree_helpers::_S_insert_and_rebalance
+	  (__insert_left, __z, __p, this->_M_impl._M_header);
 	++_M_impl._M_node_count;
 	return iterator(__z);
       }
@@ -1851,10 +2614,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _KeyOfValue()(__v)));
 
-      _Link_type __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
+      _Base_ptr __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Tree_helpers::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -1872,7 +2635,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_insert_equal_lower(const _Val& __v)
 #endif
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       while (__x != 0)
 	{
@@ -1886,12 +2649,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Key, typename _Val, typename _KoV,
 	   typename _Compare, typename _Alloc>
     template<bool _MoveValues, typename _NodeGen>
-      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Link_type
+      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Base_ptr
       _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::
-      _M_copy(_Link_type __x, _Base_ptr __p, _NodeGen& __node_gen)
+      _M_copy(_Base_ptr __x, _Base_ptr __p, _NodeGen& __node_gen)
       {
 	// Structural copy. __x and __p must be non-null.
-	_Link_type __top = _M_clone_node<_MoveValues>(__x, __node_gen);
+	_Base_ptr __top = _M_clone_node<_MoveValues>(__x, __node_gen);
 	__top->_M_parent = __p;
 
 	__try
@@ -1904,7 +2667,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 	    while (__x != 0)
 	      {
-		_Link_type __y = _M_clone_node<_MoveValues>(__x, __node_gen);
+		_Base_ptr __y = _M_clone_node<_MoveValues>(__x, __node_gen);
 		__p->_M_left = __y;
 		__y->_M_parent = __p;
 		if (__x->_M_right)
@@ -1926,13 +2689,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     void
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_erase(_Link_type __x)
+    _M_erase(_Base_ptr __x)
     {
       // Erase without rebalancing.
       while (__x != 0)
 	{
 	  _M_erase(_S_right(__x));
-	  _Link_type __y = _S_left(__x);
+	  _Base_ptr __y = _S_left(__x);
 	  _M_drop_node(__x);
 	  __x = __y;
 	}
@@ -1943,10 +2706,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
 		      _Compare, _Alloc>::iterator
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Link_type __x, _Base_ptr __y,
+    _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k)
     {
-      while (__x != 0)
+      while (__x)
 	if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	  __y = __x, __x = _S_left(__x);
 	else
@@ -1959,10 +2722,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
 		      _Compare, _Alloc>::const_iterator
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_lower_bound(_Const_Base_ptr __x, _Const_Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	  __y = __x, __x = _S_left(__x);
 	else
@@ -1975,10 +2738,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
 		      _Compare, _Alloc>::iterator
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Link_type __x, _Base_ptr __y,
+    _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k)
     {
-      while (__x != 0)
+      while (__x)
 	if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	  __y = __x, __x = _S_left(__x);
 	else
@@ -1991,10 +2754,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
 		      _Compare, _Alloc>::const_iterator
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_upper_bound(_Const_Base_ptr __x, _Const_Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	  __y = __x, __x = _S_left(__x);
 	else
@@ -2011,9 +2774,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k)
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
 	    __x = _S_right(__x);
@@ -2021,7 +2784,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Link_type __xu(__x);
+	      _Base_ptr __xu(__x);
 	      _Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
@@ -2043,7 +2806,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k) const
     {
-      _Const_Link_type __x = _M_begin();
+      _Const_Base_ptr __x = _M_begin();
       _Const_Base_ptr __y = _M_end();
       while (__x != 0)
 	{
@@ -2053,7 +2816,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Const_Link_type __xu(__x);
+	      _Const_Base_ptr __xu(__x);
 	      _Const_Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
@@ -2107,10 +2870,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_unique_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       bool __comp = true;
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __comp = _M_impl._M_key_compare(__k, _S_key(__x));
@@ -2139,9 +2902,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_equal_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = _M_impl._M_key_compare(__k, _S_key(__x)) ?
@@ -2373,15 +3136,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Link_type __z)
+    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Base_ptr __z)
     -> iterator
     {
       bool __insert_left = (__x != 0 || __p == _M_end()
 			    || _M_impl._M_key_compare(_S_key(__z),
 						      _S_key(__p)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Tree_helpers::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -2390,15 +3153,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_lower_node(_Base_ptr __p, _Link_type __z)
+    _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z)
     -> iterator
     {
       bool __insert_left = (__p == _M_end()
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _S_key(__z)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Tree_helpers::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -2407,10 +3170,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_equal_lower_node(_Link_type __z)
+    _M_insert_equal_lower_node(_Base_ptr __z)
     -> iterator
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       while (__x != 0)
 	{
@@ -2487,10 +3250,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     _M_erase_aux(const_iterator __position)
     {
-      _Link_type __y =
-	static_cast<_Link_type>(_Rb_tree_rebalance_for_erase
-				(const_cast<_Base_ptr>(__position._M_node),
-				 this->_M_impl._M_header));
+      _Base_ptr __y = _Tree_helpers::_S_rebalance_for_erase
+	(__position._M_const_cast()._M_node, this->_M_impl._M_header);
       _M_drop_node(__y);
       --_M_impl._M_node_count;
     }
@@ -2574,9 +3335,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       unsigned int __len = _Rb_tree_black_count(_M_leftmost(), _M_root());
       for (const_iterator __it = begin(); __it != end(); ++__it)
 	{
-	  _Const_Link_type __x = static_cast<_Const_Link_type>(__it._M_node);
-	  _Const_Link_type __L = _S_left(__x);
-	  _Const_Link_type __R = _S_right(__x);
+	  _Const_Node_ptr __x = static_cast<_Const_Node_ptr>(__it._M_node);
+	  _Const_Node_ptr __L = _S_left(__x);
+	  _Const_Node_ptr __R = _S_right(__x);
 
 	  if (__x->_M_color == _S_red)
 	    if ((__L && __L->_M_color == _S_red)
@@ -2592,9 +3353,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    return false;
 	}
 
-      if (_M_leftmost() != _Rb_tree_node_base::_S_minimum(_M_root()))
+      if (_M_leftmost() != _Tree_helpers::_S_minimum(_M_root()))
 	return false;
-      if (_M_rightmost() != _Rb_tree_node_base::_S_maximum(_M_root()))
+      if (_M_rightmost() != _Tree_helpers::_S_maximum(_M_root()))
 	return false;
       return true;
     }
diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..758beedccfd
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
@@ -0,0 +1,44 @@
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct H
+{
+  std::size_t
+  operator()(const T& t) const noexcept
+  { return t.i; }
+};
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::map<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::map<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( v.begin()->second == 0 );
+  VERIFY( ++v.begin() == v.end() );
+  v.insert({ T { 1 }, 1 });
+  VERIFY( v.size() == 2 );
+  VERIFY( v.find(T()) != v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..1a9e198531f
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
@@ -0,0 +1,40 @@
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct H
+{
+  std::size_t
+  operator()(const T& t) const noexcept
+  { return t.i; }
+};
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multimap<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::multimap<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..d4796e37672
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
@@ -0,0 +1,39 @@
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct H
+{
+  std::size_t
+  operator()(const T& t) const noexcept
+  { return t.i; }
+};
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multiset<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::multiset<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..1cf479083eb
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
@@ -0,0 +1,39 @@
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct H
+{
+  std::size_t
+  operator()(const T& t) const noexcept
+  { return t.i; }
+};
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::set<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::set<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
  
Jonathan Wakely Oct. 17, 2024, 8:39 p.m. UTC | #5
On Thu, 17 Oct 2024 at 20:52, François Dumont <frs.dumont@gmail.com> wrote:

> Here is an updated version that compiles, I think, all your feedbacks.
> It's much cleaner indeed.
>

Thanks, I'll take a look tomorrow.


> It's also tested in C++98/17/23.
>
> I'm surprised that we do not need to consider potential
> allocator::const_pointer.
>
Do you mean consider the case where Alloc::const_pointer is not the same
type as rebinding 'pointer' to a const element type?

We don't need to consider that because we never get a 'const_pointer' from
the allocator, and we never need to pass a 'const_pointer' to the
allocator. The allocator's 'allocate' and 'deallocate' members both work
with the 'pointer' type, so we only need to use that type when interacting
with the allocator. For all the other uses, such as _Const_Node_ptr, what
we need is a pointer-to-const that's compatible with the allocator's
pointer type. It doesn't actually matter if it's the same type as
allocator_traits<Alloc>::const_pointer, because we don't need


> Is there a plan to deprecate it ?
>

No, although I think that would be possible. Nothing in the allocator
requirements ever uses that type or cares what it is, as long as it's
convertible to  'const_void_pointer', and 'pointer' is convertible to
'const_pointer'.


> And if not, should not alloc traits const_pointer be per default a rebind
> of pointer for const element_type like in the __add_const_to_ptr you made
> me add ? I can try to work on a patch for that if needed.
>

No, allocator_traits is defined correctly as the standard requires. If an
allocator A defines a 'A::const_pointer' typedef, then that is used. If
not, then 'allocator_traits<A>::const_pointer' defaults to rebinding the
non-const 'allocator_traits<A>::pointer' type.
  
Jonathan Wakely Oct. 17, 2024, 9:11 p.m. UTC | #6
On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:

>
>
> On Thu, 17 Oct 2024 at 20:52, François Dumont <frs.dumont@gmail.com>
> wrote:
>
>> Here is an updated version that compiles, I think, all your feedbacks.
>> It's much cleaner indeed.
>>
>
> Thanks, I'll take a look tomorrow.
>
>
>> It's also tested in C++98/17/23.
>>
>> I'm surprised that we do not need to consider potential
>> allocator::const_pointer.
>>
> Do you mean consider the case where Alloc::const_pointer is not the same
> type as rebinding 'pointer' to a const element type?
>
> We don't need to consider that because we never get a 'const_pointer' from
> the allocator, and we never need to pass a 'const_pointer' to the
> allocator. The allocator's 'allocate' and 'deallocate' members both work
> with the 'pointer' type, so we only need to use that type when interacting
> with the allocator. For all the other uses, such as _Const_Node_ptr, what
> we need is a pointer-to-const that's compatible with the allocator's
> pointer type. It doesn't actually matter if it's the same type as
> allocator_traits<Alloc>::const_pointer, because we don't need
>

Sorry, I sent the email before finishing that thought!

... we don't need to pass a const_pointer to anything, we only need it for
the container's own purposes.

But thinking about it some more, do we even need a const-pointer for the
container?  Currently the const_iterator stores a const-pointer, and some
members like _M_root() and _M_leftmost() return a const-pointer. But they
don't need to. The nodes are all pointed to by a non-const _Base_ptr, none
of the storage managed by the container is const. We could just use the
non-const pointers everywhere, which would make things much simpler!

The const_iterator stores a const_pointer, and returns a const-pointer from
operator->(), so maybe _Rb_tree_const_piterator should take the allocator's
const_pointer as its template argument, instead of the non-const ValPtr. So
the trait would take two pointer types, but the const one would only be
used for the const iterator:

template<typename _ValPtr, typename _CValPtr>
  struct _Rb_tree_node_traits
  {
    using _Node_base = _Rb_tree_pnode_base<_ValPtr>;
    using _Node_type = _Rb_tree_pnode<_ValPtr>;
    using _Header_t = _Rb_tree_pheader<_Node_base>;
    using _Iterator_t = _Rb_tree_piterator<_ValPtr>;
    using _Const_iterator_t = _Rb_tree_const_piterator<_CValPtr>;
  };

Would that work? I can experiment with that if you like.



>
>
>> Is there a plan to deprecate it ?
>>
>
> No, although I think that would be possible. Nothing in the allocator
> requirements ever uses that type or cares what it is, as long as it's
> convertible to  'const_void_pointer', and 'pointer' is convertible to
> 'const_pointer'.
>
>
>> And if not, should not alloc traits const_pointer be per default a rebind
>> of pointer for const element_type like in the __add_const_to_ptr you made
>> me add ? I can try to work on a patch for that if needed.
>>
>
> No, allocator_traits is defined correctly as the standard requires. If an
> allocator A defines a 'A::const_pointer' typedef, then that is used. If
> not, then 'allocator_traits<A>::const_pointer' defaults to rebinding the
> non-const 'allocator_traits<A>::pointer' type.
>
>
>
  
François Dumont Oct. 21, 2024, 4:56 a.m. UTC | #7
On 17/10/2024 23:11, Jonathan Wakely wrote:
>
>
> On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely <jwakely.gcc@gmail.com> 
> wrote:
>
>
>
>     On Thu, 17 Oct 2024 at 20:52, François Dumont
>     <frs.dumont@gmail.com> wrote:
>
>         Here is an updated version that compiles, I think, all your
>         feedbacks. It's much cleaner indeed.
>
>
>     Thanks, I'll take a look tomorrow.
>
>         It's also tested in C++98/17/23.
>
>         I'm surprised that we do not need to consider potential
>         allocator::const_pointer.
>
>     Do you mean consider the case where Alloc::const_pointer is not
>     the same type as rebinding 'pointer' to a const element type?
>
Yes, exactly.


>
>     We don't need to consider that because we never get a
>     'const_pointer' from the allocator, and we never need to pass a
>     'const_pointer' to the allocator. The allocator's 'allocate' and
>     'deallocate' members both work with the 'pointer' type, so we only
>     need to use that type when interacting with the allocator. For all
>     the other uses, such as _Const_Node_ptr, what we need is a
>     pointer-to-const that's compatible with the allocator's pointer
>     type. It doesn't actually matter if it's the same type as
>     allocator_traits<Alloc>::const_pointer, because we don't need
>
>
> Sorry, I sent the email before finishing that thought!
>
> ... we don't need to pass a const_pointer to anything, we only need it 
> for the container's own purposes.
>
> But thinking about it some more, do we even need a const-pointer for 
> the container?  Currently the const_iterator stores a const-pointer, 
> and some members like _M_root() and _M_leftmost() return a 
> const-pointer. But they don't need to. The nodes are all pointed to by 
> a non-const _Base_ptr, none of the storage managed by the container is 
> const. We could just use the non-const pointers everywhere, which 
> would make things much simpler!

I just reproduced the current code approach on the const_iterator, I 
though it was more consistent that const_iterator was storing a 
const_pointer. But for the _Rb_tree_const_piterator we can indeed just 
store an allocator pointer. I'll give it a try.


>
> The const_iterator stores a const_pointer, and returns a const-pointer 
> from operator->(), so maybe _Rb_tree_const_piterator should take the 
> allocator's const_pointer as its template argument, instead of the 
> non-const ValPtr. So the trait would take two pointer types, but the 
> const one would only be used for the const iterator:
>
> template<typename _ValPtr, typename _CValPtr>
>   struct _Rb_tree_node_traits
>   {
>     using _Node_base = _Rb_tree_pnode_base<_ValPtr>;
>     using _Node_type = _Rb_tree_pnode<_ValPtr>;
>     using _Header_t = _Rb_tree_pheader<_Node_base>;
>     using _Iterator_t = _Rb_tree_piterator<_ValPtr>;
>     using _Const_iterator_t = _Rb_tree_const_piterator<_CValPtr>;
>   };
>
> Would that work? I can experiment with that if you like.

Seems contradictory with the previous proposal to store allocator 
pointer in const_iterator. I prefer to experiment with the latter 
approach so feel free to experiment any other approach.


>
>         Is there a plan to deprecate it ?
>
>
>     No, although I think that would be possible. Nothing in the
>     allocator requirements ever uses that type or cares what it is, as
>     long as it's convertible to  'const_void_pointer', and 'pointer'
>     is convertible to 'const_pointer'.
>
>         And if not, should not alloc traits const_pointer be per
>         default a rebind of pointer for const element_type like in the
>         __add_const_to_ptr you made me add ? I can try to work on a
>         patch for that if needed.
>
>
>     No, allocator_traits is defined correctly as the standard
>     requires. If an allocator A defines a 'A::const_pointer' typedef,
>     then that is used. If not, then
>     'allocator_traits<A>::const_pointer' defaults to rebinding the
>     non-const 'allocator_traits<A>::pointer' type.
>
Indeed, I hadn't look close enough.
  
François Dumont Nov. 4, 2024, 6:30 p.m. UTC | #8
On 21/10/2024 06:56, François Dumont wrote:
>
>
> On 17/10/2024 23:11, Jonathan Wakely wrote:
>>
>>
>> On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely <jwakely.gcc@gmail.com> 
>> wrote:
>>
>>
>>
>>     On Thu, 17 Oct 2024 at 20:52, François Dumont
>>     <frs.dumont@gmail.com> wrote:
>>
>>         Here is an updated version that compiles, I think, all your
>>         feedbacks. It's much cleaner indeed.
>>
>>
>>     Thanks, I'll take a look tomorrow.
>>
>>         It's also tested in C++98/17/23.
>>
>>         I'm surprised that we do not need to consider potential
>>         allocator::const_pointer.
>>
>>     Do you mean consider the case where Alloc::const_pointer is not
>>     the same type as rebinding 'pointer' to a const element type?
>>
> Yes, exactly.
>
>
>>
>>     We don't need to consider that because we never get a
>>     'const_pointer' from the allocator, and we never need to pass a
>>     'const_pointer' to the allocator. The allocator's 'allocate' and
>>     'deallocate' members both work with the 'pointer' type, so we
>>     only need to use that type when interacting with the allocator.
>>     For all the other uses, such as _Const_Node_ptr, what we need is
>>     a pointer-to-const that's compatible with the allocator's pointer
>>     type. It doesn't actually matter if it's the same type as
>>     allocator_traits<Alloc>::const_pointer, because we don't need
>>
>>
>> Sorry, I sent the email before finishing that thought!
>>
>> ... we don't need to pass a const_pointer to anything, we only need 
>> it for the container's own purposes.
>>
>> But thinking about it some more, do we even need a const-pointer for 
>> the container?  Currently the const_iterator stores a const-pointer, 
>> and some members like _M_root() and _M_leftmost() return a 
>> const-pointer. But they don't need to. The nodes are all pointed to 
>> by a non-const _Base_ptr, none of the storage managed by the 
>> container is const.

Except _M_impl._M_header in a const context.

Using const_cast would result in a similar UB that you fixed on 
_GLIBCXX_DEBUG containers, no ?

I can provide a version with the const_cast but I'm not sure it worth it.

François
  
Jonathan Wakely Nov. 4, 2024, 6:45 p.m. UTC | #9
On Mon, 4 Nov 2024 at 18:30, François Dumont <frs.dumont@gmail.com> wrote:
>
>
> On 21/10/2024 06:56, François Dumont wrote:
>
>
> On 17/10/2024 23:11, Jonathan Wakely wrote:
>
>
>
> On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
>>
>>
>>
>> On Thu, 17 Oct 2024 at 20:52, François Dumont <frs.dumont@gmail.com> wrote:
>>>
>>> Here is an updated version that compiles, I think, all your feedbacks. It's much cleaner indeed.
>>
>>
>> Thanks, I'll take a look tomorrow.
>>
>>>
>>> It's also tested in C++98/17/23.
>>>
>>> I'm surprised that we do not need to consider potential allocator::const_pointer.
>>
>> Do you mean consider the case where Alloc::const_pointer is not the same type as rebinding 'pointer' to a const element type?
>
> Yes, exactly.
>
>
>>
>> We don't need to consider that because we never get a 'const_pointer' from the allocator, and we never need to pass a 'const_pointer' to the allocator. The allocator's 'allocate' and 'deallocate' members both work with the 'pointer' type, so we only need to use that type when interacting with the allocator. For all the other uses, such as _Const_Node_ptr, what we need is a pointer-to-const that's compatible with the allocator's pointer type. It doesn't actually matter if it's the same type as allocator_traits<Alloc>::const_pointer, because we don't need
>
>
> Sorry, I sent the email before finishing that thought!
>
> ... we don't need to pass a const_pointer to anything, we only need it for the container's own purposes.
>
> But thinking about it some more, do we even need a const-pointer for the container?  Currently the const_iterator stores a const-pointer, and some members like _M_root() and _M_leftmost() return a const-pointer. But they don't need to. The nodes are all pointed to by a non-const _Base_ptr, none of the storage managed by the container is const.
>
> Except _M_impl._M_header in a const context.
>
> Using const_cast would result in a similar UB that you fixed on _GLIBCXX_DEBUG containers, no ?

We only use a pointer to _M_header for the past-the-end iterator,
which is not dereferenceable. It's OK to do the const_cast, it's not
OK to write something through that pointer/reference if the object is
really const. But you can't write through a past-the-end pointer
anyway.


>
> I can provide a version with the const_cast but I'm not sure it worth it.
>
> François
>
>
  
François Dumont Nov. 4, 2024, 9:34 p.m. UTC | #10
On 04/11/2024 19:45, Jonathan Wakely wrote:
> On Mon, 4 Nov 2024 at 18:30, François Dumont <frs.dumont@gmail.com> wrote:
>>
>> On 21/10/2024 06:56, François Dumont wrote:
>>
>>
>> On 17/10/2024 23:11, Jonathan Wakely wrote:
>>
>>
>>
>> On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
>>>
>>>
>>> On Thu, 17 Oct 2024 at 20:52, François Dumont <frs.dumont@gmail.com> wrote:
>>>> Here is an updated version that compiles, I think, all your feedbacks. It's much cleaner indeed.
>>>
>>> Thanks, I'll take a look tomorrow.
>>>
>>>> It's also tested in C++98/17/23.
>>>>
>>>> I'm surprised that we do not need to consider potential allocator::const_pointer.
>>> Do you mean consider the case where Alloc::const_pointer is not the same type as rebinding 'pointer' to a const element type?
>> Yes, exactly.
>>
>>
>>> We don't need to consider that because we never get a 'const_pointer' from the allocator, and we never need to pass a 'const_pointer' to the allocator. The allocator's 'allocate' and 'deallocate' members both work with the 'pointer' type, so we only need to use that type when interacting with the allocator. For all the other uses, such as _Const_Node_ptr, what we need is a pointer-to-const that's compatible with the allocator's pointer type. It doesn't actually matter if it's the same type as allocator_traits<Alloc>::const_pointer, because we don't need
>>
>> Sorry, I sent the email before finishing that thought!
>>
>> ... we don't need to pass a const_pointer to anything, we only need it for the container's own purposes.
>>
>> But thinking about it some more, do we even need a const-pointer for the container?  Currently the const_iterator stores a const-pointer, and some members like _M_root() and _M_leftmost() return a const-pointer. But they don't need to. The nodes are all pointed to by a non-const _Base_ptr, none of the storage managed by the container is const.
>>
>> Except _M_impl._M_header in a const context.
>>
>> Using const_cast would result in a similar UB that you fixed on _GLIBCXX_DEBUG containers, no ?
> We only use a pointer to _M_header for the past-the-end iterator,
> which is not dereferenceable. It's OK to do the const_cast, it's not
> OK to write something through that pointer/reference if the object is
> really const. But you can't write through a past-the-end pointer
> anyway.
>
One last question then to complete this work, is it fine to change 
_Rb_tree_const_iterator::_M_node to _Base_ptr ? No abi issue in doing so ?
  
Jonathan Wakely Nov. 12, 2024, 2:56 p.m. UTC | #11
On Mon, 4 Nov 2024 at 21:34, François Dumont <frs.dumont@gmail.com> wrote:
>
>
> On 04/11/2024 19:45, Jonathan Wakely wrote:
> > On Mon, 4 Nov 2024 at 18:30, François Dumont <frs.dumont@gmail.com> wrote:
> >>
> >> On 21/10/2024 06:56, François Dumont wrote:
> >>
> >>
> >> On 17/10/2024 23:11, Jonathan Wakely wrote:
> >>
> >>
> >>
> >> On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
> >>>
> >>>
> >>> On Thu, 17 Oct 2024 at 20:52, François Dumont <frs.dumont@gmail.com> wrote:
> >>>> Here is an updated version that compiles, I think, all your feedbacks. It's much cleaner indeed.
> >>>
> >>> Thanks, I'll take a look tomorrow.
> >>>
> >>>> It's also tested in C++98/17/23.
> >>>>
> >>>> I'm surprised that we do not need to consider potential allocator::const_pointer.
> >>> Do you mean consider the case where Alloc::const_pointer is not the same type as rebinding 'pointer' to a const element type?
> >> Yes, exactly.
> >>
> >>
> >>> We don't need to consider that because we never get a 'const_pointer' from the allocator, and we never need to pass a 'const_pointer' to the allocator. The allocator's 'allocate' and 'deallocate' members both work with the 'pointer' type, so we only need to use that type when interacting with the allocator. For all the other uses, such as _Const_Node_ptr, what we need is a pointer-to-const that's compatible with the allocator's pointer type. It doesn't actually matter if it's the same type as allocator_traits<Alloc>::const_pointer, because we don't need
> >>
> >> Sorry, I sent the email before finishing that thought!
> >>
> >> ... we don't need to pass a const_pointer to anything, we only need it for the container's own purposes.
> >>
> >> But thinking about it some more, do we even need a const-pointer for the container?  Currently the const_iterator stores a const-pointer, and some members like _M_root() and _M_leftmost() return a const-pointer. But they don't need to. The nodes are all pointed to by a non-const _Base_ptr, none of the storage managed by the container is const.
> >>
> >> Except _M_impl._M_header in a const context.
> >>
> >> Using const_cast would result in a similar UB that you fixed on _GLIBCXX_DEBUG containers, no ?
> > We only use a pointer to _M_header for the past-the-end iterator,
> > which is not dereferenceable. It's OK to do the const_cast, it's not
> > OK to write something through that pointer/reference if the object is
> > really const. But you can't write through a past-the-end pointer
> > anyway.
> >
> One last question then to complete this work, is it fine to change
> _Rb_tree_const_iterator::_M_node to _Base_ptr ? No abi issue in doing so ?

I thought I'd sent a reply to this question, but I don't see it in the
archives, sorry.

So changing from _Const_Base_ptr to _Base_ptr? Yes, I think that's OK.
  
François Dumont Nov. 18, 2024, 5:57 a.m. UTC | #12
Hi

Here is a new proposal with all the cleanup regarding _Const_Base_ptr 
that makes support of allocator's fancy pointer type simpler.

Also submitted as PR:

https://forge.sourceware.org/gcc/gcc-TEST/pulls/27

    libstdc++: Add fancy pointer support in map and set

     Support fancy allocator pointer type in std::_Rb_tree<>.

     In case of fancy pointer type the container is now storing the 
pointer to
     _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.

     Many methods are adapted to take and return _Base_ptr in place of 
_Link_type
     which has been renamed into _Node_ptr.

     As all node are stored as _Base_ptr have all methods working with 
this type
     and remove _Const_Base_ptr and all methods associated to it.

     libstdc++-v3/ChangeLog:

             * include/bits/stl_set.h (std::set<>::upper_bound<>(const 
_Kt&) const): Fix
             decltype typo to use const_iterator.
             * include/bits/stl_tree.h
             (_Rb_tree_ptr_traits<>): New.
             (_Rb_tree_pnode_base<>): New.
             (_Rb_tree_node_base): Inherit from latter.
             (_Rb_tree_node_base::_Const_Base_ptr): Remove.
             (_Rb_tree_node_base::_S_minimum(_Const_Base_ptr)): Remove.
             (_Rb_tree_node_base::_S_maximum(_Const_Base_ptr)): Remove.
             (_Rb_tree_pheader): New.
             (_Rb_tree_header): Inherit from latter.
             (_Rb_tree_node_val): New.
             (_Rb_tree_node): Inherit from latter.
             (_Rb_tree_pnode): New.
             (_Rb_tree_iterator<>::_Link_type): Rename into...
             (_Rb_tree_iterator<>::_Node_ptr): ...this.
             (_Rb_tree_const_iterator<>::_Link_type): Rename into...
             (_Rb_tree_const_iterator<>::_Node_ptr): ...this.
             (_Rb_tree_const_iterator<>::_M_node): Change type into 
_Base_ptr.
             (_Rb_tree_const_iterator<>::_M_const_cast): Remove.
             (_Rb_tree_helpers<>): New.
             (_Rb_tree_piterator): New.
             (_Rb_tree_const_piterator): New.
             (_Rb_tree_node_traits<>): New.
             (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
             (_Rb_tree<>::_Const_Base_ptr): Remove.
             (_Rb_tree): Adapt to generalize usage of _Base_ptr in place 
of _Link_type.
             (_Rb_tree<>::_M_mbegin): Remove.
             (_Rb_tree<>::_S_left(_Const_Base_ptr)): Remove.
             (_Rb_tree<>::_S_right(_Const_Base_ptr)): Remove.
             (_Rb_tree<>::_S_maximum(_Const_Base_ptr)): Remove.
             (_Rb_tree<>::_S_minimum(_Const_Base_ptr)): Remove.
             * testsuite/23_containers/map/allocator/ext_ptr.cc: New 
test case.
             * testsuite/23_containers/multimap/allocator/ext_ptr.cc: 
New test case.
             * testsuite/23_containers/multiset/allocator/ext_ptr.cc: 
New test case.
             * testsuite/23_containers/set/allocator/ext_ptr.cc: New 
test case.

Tested under Linux x64.


Note that I've also run the 23_containers tests on map, multimap, 
multiset and set tweaking implementation
so that new types are being used when C++11 or later even if allocator 
pointer type is a C pointer.

Ok to commit ?

François

On 12/11/2024 15:56, Jonathan Wakely wrote:
> On Mon, 4 Nov 2024 at 21:34, François Dumont <frs.dumont@gmail.com> wrote:
>>
>> On 04/11/2024 19:45, Jonathan Wakely wrote:
>>> On Mon, 4 Nov 2024 at 18:30, François Dumont <frs.dumont@gmail.com> wrote:
>>>> On 21/10/2024 06:56, François Dumont wrote:
>>>>
>>>>
>>>> On 17/10/2024 23:11, Jonathan Wakely wrote:
>>>>
>>>>
>>>>
>>>> On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
>>>>>
>>>>> On Thu, 17 Oct 2024 at 20:52, François Dumont <frs.dumont@gmail.com> wrote:
>>>>>> Here is an updated version that compiles, I think, all your feedbacks. It's much cleaner indeed.
>>>>> Thanks, I'll take a look tomorrow.
>>>>>
>>>>>> It's also tested in C++98/17/23.
>>>>>>
>>>>>> I'm surprised that we do not need to consider potential allocator::const_pointer.
>>>>> Do you mean consider the case where Alloc::const_pointer is not the same type as rebinding 'pointer' to a const element type?
>>>> Yes, exactly.
>>>>
>>>>
>>>>> We don't need to consider that because we never get a 'const_pointer' from the allocator, and we never need to pass a 'const_pointer' to the allocator. The allocator's 'allocate' and 'deallocate' members both work with the 'pointer' type, so we only need to use that type when interacting with the allocator. For all the other uses, such as _Const_Node_ptr, what we need is a pointer-to-const that's compatible with the allocator's pointer type. It doesn't actually matter if it's the same type as allocator_traits<Alloc>::const_pointer, because we don't need
>>>> Sorry, I sent the email before finishing that thought!
>>>>
>>>> ... we don't need to pass a const_pointer to anything, we only need it for the container's own purposes.
>>>>
>>>> But thinking about it some more, do we even need a const-pointer for the container?  Currently the const_iterator stores a const-pointer, and some members like _M_root() and _M_leftmost() return a const-pointer. But they don't need to. The nodes are all pointed to by a non-const _Base_ptr, none of the storage managed by the container is const.
>>>>
>>>> Except _M_impl._M_header in a const context.
>>>>
>>>> Using const_cast would result in a similar UB that you fixed on _GLIBCXX_DEBUG containers, no ?
>>> We only use a pointer to _M_header for the past-the-end iterator,
>>> which is not dereferenceable. It's OK to do the const_cast, it's not
>>> OK to write something through that pointer/reference if the object is
>>> really const. But you can't write through a past-the-end pointer
>>> anyway.
>>>
>> One last question then to complete this work, is it fine to change
>> _Rb_tree_const_iterator::_M_node to _Base_ptr ? No abi issue in doing so ?
> I thought I'd sent a reply to this question, but I don't see it in the
> archives, sorry.
>
> So changing from _Const_Base_ptr to _Base_ptr? Yes, I think that's OK.
>
diff --git a/libstdc++-v3/include/bits/stl_set.h b/libstdc++-v3/include/bits/stl_set.h
index c0eb4dbf65f..3d17626f330 100644
--- a/libstdc++-v3/include/bits/stl_set.h
+++ b/libstdc++-v3/include/bits/stl_set.h
@@ -875,7 +875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       template<typename _Kt>
 	auto
 	upper_bound(const _Kt& __x) const
-	-> decltype(iterator(_M_t._M_upper_bound_tr(__x)))
+	-> decltype(const_iterator(_M_t._M_upper_bound_tr(__x)))
 	{ return const_iterator(_M_t._M_upper_bound_tr(__x)); }
 #endif
       ///@}
diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
index bc27e191e8b..c6cba99726d 100644
--- a/libstdc++-v3/include/bits/stl_tree.h
+++ b/libstdc++-v3/include/bits/stl_tree.h
@@ -96,44 +96,100 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   enum _Rb_tree_color { _S_red = false, _S_black = true };
 
-  struct _Rb_tree_node_base
-  {
-    typedef _Rb_tree_node_base* _Base_ptr;
-    typedef const _Rb_tree_node_base* _Const_Base_ptr;
-
-    _Rb_tree_color	_M_color;
-    _Base_ptr		_M_parent;
-    _Base_ptr		_M_left;
-    _Base_ptr		_M_right;
-
-    static _Base_ptr
-    _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+#if __cplusplus >= 201103L
+  template<typename _Ptr>
+    struct _Rb_tree_ptr_traits
     {
-      while (__x->_M_left != 0) __x = __x->_M_left;
-      return __x;
-    }
+      template<typename _Tp>
+	struct __rebind
+	{ using __type = __ptr_rebind<_Ptr, _Tp>; };
+
+      template<typename _BasePtr>
+	static _Ptr
+	_S_ptr_to(_BasePtr __this) noexcept
+	{ return pointer_traits<_Ptr>::pointer_to(*__this); }
+
+      template<typename _BasePtr, typename _UpPtr>
+	static _UpPtr
+	_S_up_ptr_to(_BasePtr __this) noexcept
+	{
+	  using __ptr_traits = pointer_traits<_UpPtr>;
+	  using __up_type = typename __ptr_traits::element_type;
+	  auto __up_this = static_cast<__up_type*>(__this);
+	  return __ptr_traits::pointer_to(*__up_this);
+	}
+    };
+#else
+  template<typename _Ptr>
+    struct _Rb_tree_ptr_traits;
+#endif
 
-    static _Const_Base_ptr
-    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
+  template<typename _Tp>
+    struct _Rb_tree_ptr_traits<_Tp*>
     {
-      while (__x->_M_left != 0) __x = __x->_M_left;
-      return __x;
-    }
+      template<typename>
+	struct __rebind
+	{ typedef _Tp* __type; };
+
+      template<typename _BasePtr>
+	static _Tp*
+	_S_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
+	{ return static_cast<_Tp*>(__this); }
+
+      template<typename _BasePtr, typename _UpPtr>
+	static _UpPtr
+	_S_up_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
+	{ return static_cast<_UpPtr>(__this); }
+    };
 
-    static _Base_ptr
-    _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+  template<typename _BasePtr>
+    struct _Rb_tree_pnode_base
     {
-      while (__x->_M_right != 0) __x = __x->_M_right;
-      return __x;
-    }
+      typedef typename _Rb_tree_ptr_traits<_BasePtr>::template
+	__rebind<_Rb_tree_pnode_base>::__type _Base_ptr;
 
-    static _Const_Base_ptr
-    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-    {
-      while (__x->_M_right != 0) __x = __x->_M_right;
-      return __x;
-    }
-  };
+      _Base_ptr
+      _M_self_ptr() _GLIBCXX_NOEXCEPT
+      { return _Rb_tree_ptr_traits<_Base_ptr>::_S_ptr_to(this); }
+
+      _Base_ptr
+      _M_self_ptr() const _GLIBCXX_NOEXCEPT
+      {
+	_Rb_tree_pnode_base* __self = const_cast<_Rb_tree_pnode_base*>(this);
+	return __self->_M_self_ptr();
+      }
+
+      template<typename _NodePtr>
+	_NodePtr
+	_M_node_ptr() _GLIBCXX_NOEXCEPT
+	{
+	  return _Rb_tree_ptr_traits<_Base_ptr>::template
+	    _S_up_ptr_to<_Rb_tree_pnode_base*, _NodePtr>(this);
+	}
+
+      _Rb_tree_color	_M_color;
+      _Base_ptr		_M_parent;
+      _Base_ptr		_M_left;
+      _Base_ptr		_M_right;
+
+      static _Base_ptr
+      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      {
+	while (__x->_M_left) __x = __x->_M_left;
+	return __x;
+      }
+
+      static _Base_ptr
+      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      {
+	while (__x->_M_right) __x = __x->_M_right;
+	return __x;
+      }
+    };
+
+  struct _Rb_tree_node_base
+    : _Rb_tree_pnode_base<_Rb_tree_node_base*>
+  { };
 
   // Helper type offering value initialization guarantee on the compare functor.
   template<typename _Key_compare>
@@ -163,81 +219,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   // Helper type to manage default initialization of node count and header.
-  struct _Rb_tree_header
-  {
-    _Rb_tree_node_base	_M_header;
-    size_t		_M_node_count; // Keeps track of size of tree.
-
-    _Rb_tree_header() _GLIBCXX_NOEXCEPT
+  template<typename _NodeBase>
+    struct _Rb_tree_pheader
     {
-      _M_header._M_color = _S_red;
-      _M_reset();
-    }
+    private:
+      typedef typename _NodeBase::_Base_ptr		_Base_ptr;
+
+    public:
+      _NodeBase		_M_header;
+      size_t		_M_node_count; // Keeps track of size of tree.
+
+      _Rb_tree_pheader() _GLIBCXX_NOEXCEPT
+      {
+	_M_header._M_color = _S_red;
+	_M_reset();
+      }
 
 #if __cplusplus >= 201103L
-    _Rb_tree_header(_Rb_tree_header&& __x) noexcept
-    {
-      if (__x._M_header._M_parent != nullptr)
-	_M_move_data(__x);
-      else
-	{
-	  _M_header._M_color = _S_red;
-	  _M_reset();
-	}
-    }
+      _Rb_tree_pheader(_Rb_tree_pheader&& __x) noexcept
+      {
+	if (__x._M_header._M_parent)
+	  _M_move_data(__x);
+	else
+	  {
+	    _M_header._M_color = _S_red;
+	    _M_reset();
+	  }
+      }
 #endif
 
-    void
-    _M_move_data(_Rb_tree_header& __from)
-    {
-      _M_header._M_color = __from._M_header._M_color;
-      _M_header._M_parent = __from._M_header._M_parent;
-      _M_header._M_left = __from._M_header._M_left;
-      _M_header._M_right = __from._M_header._M_right;
-      _M_header._M_parent->_M_parent = &_M_header;
-      _M_node_count = __from._M_node_count;
-
-      __from._M_reset();
-    }
+      void
+      _M_move_data(_Rb_tree_pheader& __from)
+      {
+	_M_header._M_color = __from._M_header._M_color;
+	_M_header._M_parent = __from._M_header._M_parent;
+	_M_header._M_left = __from._M_header._M_left;
+	_M_header._M_right = __from._M_header._M_right;
+	_M_header._M_parent->_M_parent = _M_header._M_self_ptr();
+	_M_node_count = __from._M_node_count;
+
+	__from._M_reset();
+      }
 
-    void
-    _M_reset()
-    {
-      _M_header._M_parent = 0;
-      _M_header._M_left = &_M_header;
-      _M_header._M_right = &_M_header;
-      _M_node_count = 0;
-    }
-  };
+      void
+      _M_reset()
+      {
+	_M_header._M_parent = _Base_ptr();
+	_M_header._M_left = _M_header._M_right = _M_header._M_self_ptr();
+	_M_node_count = 0;
+      }
+    };
+
+  struct _Rb_tree_header : _Rb_tree_pheader<_Rb_tree_node_base>
+  { };
 
   template<typename _Val>
-    struct _Rb_tree_node : public _Rb_tree_node_base
+    struct _Rb_tree_node_val
     {
-      typedef _Rb_tree_node<_Val>* _Link_type;
-
 #if __cplusplus < 201103L
       _Val _M_value_field;
 
+      __attribute__((__always_inline__))
       _Val*
       _M_valptr()
       { return std::__addressof(_M_value_field); }
 
+      __attribute__((__always_inline__))
       const _Val*
       _M_valptr() const
       { return std::__addressof(_M_value_field); }
 #else
       __gnu_cxx::__aligned_membuf<_Val> _M_storage;
 
+      [[__gnu__::__always_inline__]]
       _Val*
       _M_valptr()
       { return _M_storage._M_ptr(); }
 
+      [[__gnu__::__always_inline__]]
       const _Val*
       _M_valptr() const
       { return _M_storage._M_ptr(); }
 #endif
     };
 
+  template<typename _Val>
+    struct _Rb_tree_node
+    : _Rb_tree_node_base
+    , _Rb_tree_node_val<_Val>
+    {
+      typedef _Rb_tree_node<_Val>* _Node_ptr;
+    };
+
+#if __cplusplus >= 201103L
+  template<typename _ValPtr>
+    struct _Rb_tree_pnode
+    : _Rb_tree_pnode_base<_ValPtr>
+    , _Rb_tree_node_val<typename std::pointer_traits<_ValPtr>::element_type>
+    {
+      using _Node_ptr = __ptr_rebind<_ValPtr, _Rb_tree_pnode>;
+    };
+#endif
+
   _GLIBCXX_PURE _Rb_tree_node_base*
   _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
 
@@ -250,6 +333,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _GLIBCXX_PURE const _Rb_tree_node_base*
   _Rb_tree_decrement(const _Rb_tree_node_base* __x) throw ();
 
+  __attribute__((__nonnull__))
+  void
+  _Rb_tree_insert_and_rebalance(const bool __insert_left,
+				_Rb_tree_node_base* __x,
+				_Rb_tree_node_base* __p,
+				_Rb_tree_node_base& __header) throw ();
+
+  __attribute__((__nonnull__,__returns_nonnull__))
+  _Rb_tree_node_base*
+  _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
+			       _Rb_tree_node_base& __header) throw ();
+
   template<typename _Tp>
     struct _Rb_tree_iterator
     {
@@ -257,12 +352,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef _Tp& reference;
       typedef _Tp* pointer;
 
-      typedef bidirectional_iterator_tag iterator_category;
-      typedef ptrdiff_t			 difference_type;
+      typedef bidirectional_iterator_tag	iterator_category;
+      typedef ptrdiff_t				difference_type;
 
-      typedef _Rb_tree_iterator<_Tp>		_Self;
-      typedef _Rb_tree_node_base::_Base_ptr	_Base_ptr;
-      typedef _Rb_tree_node<_Tp>*		_Link_type;
+      typedef _Rb_tree_iterator<_Tp>			_Self;
+      typedef typename _Rb_tree_node_base::_Base_ptr	_Base_ptr;
+      typedef _Rb_tree_node<_Tp>*			_Node_ptr;
 
       _Rb_tree_iterator() _GLIBCXX_NOEXCEPT
       : _M_node() { }
@@ -273,11 +368,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       reference
       operator*() const _GLIBCXX_NOEXCEPT
-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type> (_M_node)->_M_valptr(); }
+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       _Self&
       operator++() _GLIBCXX_NOEXCEPT
@@ -334,9 +429,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef bidirectional_iterator_tag iterator_category;
       typedef ptrdiff_t			 difference_type;
 
-      typedef _Rb_tree_const_iterator<_Tp>		_Self;
-      typedef _Rb_tree_node_base::_Const_Base_ptr	_Base_ptr;
-      typedef const _Rb_tree_node<_Tp>*			_Link_type;
+      typedef _Rb_tree_const_iterator<_Tp>			_Self;
+      typedef typename _Rb_tree_node_base::_Base_ptr		_Base_ptr;
+      typedef const _Rb_tree_node<_Tp>*				_Node_ptr;
 
       _Rb_tree_const_iterator() _GLIBCXX_NOEXCEPT
       : _M_node() { }
@@ -348,17 +443,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree_const_iterator(const iterator& __it) _GLIBCXX_NOEXCEPT
       : _M_node(__it._M_node) { }
 
-      iterator
-      _M_const_cast() const _GLIBCXX_NOEXCEPT
-      { return iterator(const_cast<typename iterator::_Base_ptr>(_M_node)); }
-
       reference
       operator*() const _GLIBCXX_NOEXCEPT
-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       _Self&
       operator++() _GLIBCXX_NOEXCEPT
@@ -403,17 +494,570 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Base_ptr _M_node;
     };
 
-  __attribute__((__nonnull__))
-  void
-  _Rb_tree_insert_and_rebalance(const bool __insert_left,
-				_Rb_tree_node_base* __x,
-				_Rb_tree_node_base* __p,
-				_Rb_tree_node_base& __header) throw ();
+#if __cplusplus >= 201103L
+  template<typename _NodeBase>
+    struct _Rb_tree_helpers
+    {
+      using _Base_ptr = typename _NodeBase::_Base_ptr;
 
-  __attribute__((__nonnull__,__returns_nonnull__))
-  _Rb_tree_node_base*
-  _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
-			       _Rb_tree_node_base& __header) throw ();
+      static _Base_ptr
+      _S_minimum(_Base_ptr __x)
+      { return _NodeBase::_S_minimum(__x); }
+
+      static _Base_ptr
+      _S_maximum(_Base_ptr __x)
+      { return _NodeBase::_S_maximum(__x); }
+
+      static _Base_ptr
+      _Increment(_Base_ptr __x) noexcept
+      {
+	if (__x->_M_right)
+	  {
+	    __x = __x->_M_right;
+	    while (__x->_M_left)
+	      __x = __x->_M_left;
+	  }
+	else
+	  {
+	    _Base_ptr __y = __x->_M_parent;
+	    while (__x == __y->_M_right)
+	      {
+		__x = __y;
+		__y = __y->_M_parent;
+	      }
+	    if (__x->_M_right != __y)
+	      __x = __y;
+	  }
+	return __x;
+      }
+
+      static _Base_ptr
+      _Decrement(_Base_ptr __x) noexcept
+      {
+	if (__x->_M_color == _S_red
+	    && __x->_M_parent->_M_parent == __x)
+	  __x = __x->_M_right;
+	else if (__x->_M_left)
+	  {
+	    _Base_ptr __y = __x->_M_left;
+	    while (__y->_M_right)
+	      __y = __y->_M_right;
+	    __x = __y;
+	  }
+	else
+	  {
+	    _Base_ptr __y = __x->_M_parent;
+	    while (__x == __y->_M_left)
+	      {
+		__x = __y;
+		__y = __y->_M_parent;
+	      }
+	    __x = __y;
+	  }
+	return __x;
+      }
+
+      static void
+      _Rotate_left(_Base_ptr __x, _Base_ptr& __root)
+      {
+	const _Base_ptr __y = __x->_M_right;
+
+	__x->_M_right = __y->_M_left;
+	if (__y->_M_left)
+	  __y->_M_left->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_left)
+	  __x->_M_parent->_M_left = __y;
+	else
+	  __x->_M_parent->_M_right = __y;
+	__y->_M_left = __x;
+	__x->_M_parent = __y;
+      }
+
+      static void
+      _Rotate_right(_Base_ptr __x, _Base_ptr& __root)
+      {
+	const _Base_ptr __y = __x->_M_left;
+
+	__x->_M_left = __y->_M_right;
+	if (__y->_M_right)
+	  __y->_M_right->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_right)
+	  __x->_M_parent->_M_right = __y;
+	else
+	  __x->_M_parent->_M_left = __y;
+	__y->_M_right = __x;
+	__x->_M_parent = __y;
+      }
+
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _Base_ptr __x, _Base_ptr __p,
+			      _NodeBase& __header)
+      {
+	_Base_ptr& __root = __header._M_parent;
+
+	// Initialize fields in new node to insert.
+	__x->_M_parent = __p;
+	__x->_M_left = __x->_M_right = nullptr;
+	__x->_M_color = _S_red;
+
+	// Insert.
+	// Make new node child of parent and maintain root, leftmost and
+	// rightmost nodes.
+	// N.B. First node is always inserted left.
+	if (__insert_left)
+	  {
+	    __p->_M_left = __x; // also makes leftmost = __x when __p == &__header
+
+	    if (std::__to_address(__p) == std::addressof(__header))
+	      {
+		__header._M_parent = __x;
+		__header._M_right = __x;
+	      }
+	    else if (__p == __header._M_left)
+	      __header._M_left = __x; // maintain leftmost pointing to min node
+	  }
+	else
+	  {
+	    __p->_M_right = __x;
+
+	    if (__p == __header._M_right)
+	      __header._M_right = __x; // maintain rightmost pointing to max node
+	  }
+	// Rebalance.
+	while (__x != __root
+	       && __x->_M_parent->_M_color == _S_red)
+	  {
+	    const _Base_ptr __xpp = __x->_M_parent->_M_parent;
+
+	    if (__x->_M_parent == __xpp->_M_left)
+	      {
+		const _Base_ptr __y = __xpp->_M_right;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_right)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_left(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_right(__xpp, __root);
+		  }
+	      }
+	    else
+	      {
+		const _Base_ptr __y = __xpp->_M_left;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_left)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_right(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_left(__xpp, __root);
+		  }
+	      }
+	  }
+	__root->_M_color = _S_black;
+      }
+
+      static _Base_ptr
+      _S_rebalance_for_erase(_Base_ptr __z, _NodeBase& __header)
+      {
+	_Base_ptr& __root = __header._M_parent;
+	_Base_ptr& __leftmost = __header._M_left;
+	_Base_ptr& __rightmost = __header._M_right;
+	_Base_ptr __y = __z;
+	_Base_ptr __x{};
+	_Base_ptr __x_parent{};
+
+	if (!__y->_M_left)     // __z has at most one non-null child. y == z.
+	  __x = __y->_M_right;     // __x might be null.
+	else
+	  if (!__y->_M_right)  // __z has exactly one non-null child. y == z.
+	    __x = __y->_M_left;    // __x is not null.
+	  else
+	    {
+	      // __z has two non-null children.  Set __y to
+	      __y = __y->_M_right;   //   __z's successor.  __x might be null.
+	      while (__y->_M_left)
+		__y = __y->_M_left;
+	      __x = __y->_M_right;
+	    }
+	if (__y != __z)
+	  {
+	    // relink y in place of z.  y is z's successor
+	    __z->_M_left->_M_parent = __y;
+	    __y->_M_left = __z->_M_left;
+	    if (__y != __z->_M_right)
+	      {
+		__x_parent = __y->_M_parent;
+		if (__x)
+		  __x->_M_parent = __y->_M_parent;
+		__y->_M_parent->_M_left = __x;   // __y must be a child of _M_left
+		__y->_M_right = __z->_M_right;
+		__z->_M_right->_M_parent = __y;
+	      }
+	    else
+	      __x_parent = __y;
+	    if (__root == __z)
+	      __root = __y;
+	    else if (__z->_M_parent->_M_left == __z)
+	      __z->_M_parent->_M_left = __y;
+	    else
+	      __z->_M_parent->_M_right = __y;
+	    __y->_M_parent = __z->_M_parent;
+	    std::swap(__y->_M_color, __z->_M_color);
+	    __y = __z;
+	    // __y now points to node to be actually deleted
+	  }
+	else
+	  {                        // __y == __z
+	    __x_parent = __y->_M_parent;
+	    if (__x)
+	      __x->_M_parent = __y->_M_parent;
+	    if (__root == __z)
+	      __root = __x;
+	    else
+	      if (__z->_M_parent->_M_left == __z)
+		__z->_M_parent->_M_left = __x;
+	      else
+		__z->_M_parent->_M_right = __x;
+	    if (__leftmost == __z)
+	      {
+		if (!__z->_M_right)        // __z->_M_left must be null also
+		  __leftmost = __z->_M_parent;
+		// makes __leftmost == _M_header if __z == __root
+		else
+		  __leftmost = _S_minimum(__x);
+	      }
+	    if (__rightmost == __z)
+	      {
+		if (__z->_M_left == 0)         // __z->_M_right must be null also
+		  __rightmost = __z->_M_parent;
+		// makes __rightmost == _M_header if __z == __root
+		else                      // __x == __z->_M_left
+		  __rightmost = _S_maximum(__x);
+	      }
+	  }
+	if (__y->_M_color != _S_red)
+	  {
+	    while (__x != __root && (__x == 0 || __x->_M_color == _S_black))
+	      if (__x == __x_parent->_M_left)
+		{
+		  _Base_ptr __w = __x_parent->_M_right;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_left(__x_parent, __root);
+		      __w = __x_parent->_M_right;
+		    }
+		  if ((!__w->_M_left || __w->_M_left->_M_color == _S_black) &&
+		      (!__w->_M_right || __w->_M_right->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_right || __w->_M_right->_M_color == _S_black)
+			{
+			  __w->_M_left->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_right(__w, __root);
+			  __w = __x_parent->_M_right;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_right)
+			__w->_M_right->_M_color = _S_black;
+		      _Rotate_left(__x_parent, __root);
+		      break;
+		    }
+		}
+	      else
+		{
+		  // same as above, with _M_right <-> _M_left.
+		  _Base_ptr __w = __x_parent->_M_left;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_right(__x_parent, __root);
+		      __w = __x_parent->_M_left;
+		    }
+		  if ((!__w->_M_right || __w->_M_right->_M_color == _S_black) &&
+		      (!__w->_M_left || __w->_M_left->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_left || __w->_M_left->_M_color == _S_black)
+			{
+			  __w->_M_right->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_left(__w, __root);
+			  __w = __x_parent->_M_left;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_left)
+			__w->_M_left->_M_color = _S_black;
+		      _Rotate_right(__x_parent, __root);
+		      break;
+		    }
+		}
+	    if (__x)
+	      __x->_M_color = _S_black;
+	  }
+
+	return __y;
+      }
+  };
+#else
+  template<typename _NodeBase>
+    struct _Rb_tree_helpers;
+#endif
+
+  template<>
+    struct _Rb_tree_helpers<_Rb_tree_node_base>
+    {
+      static _Rb_tree_node_base*
+      _S_minimum(_Rb_tree_node_base* __x) _GLIBCXX_NOEXCEPT
+      { return _Rb_tree_node_base::_S_minimum(__x); }
+
+      static _Rb_tree_node_base*
+      _S_maximum(_Rb_tree_node_base* __x) _GLIBCXX_NOEXCEPT
+      { return _Rb_tree_node_base::_S_maximum(__x); }
+
+      __attribute__((__nonnull__))
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _Rb_tree_node_base* __x, _Rb_tree_node_base* __p,
+			      _Rb_tree_node_base& __header) throw ()
+      {
+	return _Rb_tree_insert_and_rebalance(__insert_left, __x, __p, __header);
+      }
+
+      __attribute__((__nonnull__))
+      static _Rb_tree_node_base*
+      _S_rebalance_for_erase(_Rb_tree_node_base* const __z,
+			     _Rb_tree_node_base& __header) throw ()
+      { return _Rb_tree_rebalance_for_erase(__z, __header); }
+    };
+
+#if __cplusplus >= 201103L
+  template<typename _ValPtr>
+    struct _Rb_tree_piterator
+    {
+      using __ptr_traits = pointer_traits<_ValPtr>;
+      using value_type = typename __ptr_traits::element_type;
+      using reference = value_type&;
+      using pointer = value_type*;
+
+      typedef bidirectional_iterator_tag iterator_category;
+      typedef ptrdiff_t			 difference_type;
+
+      typedef _Rb_tree_piterator			_Self;
+      typedef _Rb_tree_pnode_base<_ValPtr>		_Node_base;
+      typedef _Rb_tree_pnode<_ValPtr>			_Node_type;
+      typedef typename _Node_type::_Base_ptr		_Base_ptr;
+      typedef typename _Node_type::_Node_ptr		_Node_ptr;
+      typedef _Rb_tree_helpers<_Node_base>		_Tree_helpers;
+
+      _Rb_tree_piterator() _GLIBCXX_NOEXCEPT
+      : _M_node() { }
+
+      explicit
+      _Rb_tree_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      : _M_node(__x) { }
+
+      reference
+      operator*() const _GLIBCXX_NOEXCEPT
+      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
+
+      pointer
+      operator->() const _GLIBCXX_NOEXCEPT
+      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
+
+      _Self&
+      operator++() _GLIBCXX_NOEXCEPT
+      {
+	_M_node = _Tree_helpers::_Increment(_M_node);
+	return *this;
+      }
+
+      _Self
+      operator++(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	_M_node = _Tree_helpers::_Increment(_M_node);
+	return __tmp;
+      }
+
+      _Self&
+      operator--() _GLIBCXX_NOEXCEPT
+      {
+	_M_node = _Tree_helpers::_Decrement(_M_node);
+	return *this;
+      }
+
+      _Self
+      operator--(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	_M_node = _Tree_helpers::_Decrement(_M_node);
+	return __tmp;
+      }
+
+      friend bool
+      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node == __y._M_node; }
+
+#if ! __cpp_lib_three_way_comparison
+      friend bool
+      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node != __y._M_node; }
+#endif
+
+      _Base_ptr _M_node;
+    };
+
+  template<typename _ValPtr>
+    struct _Rb_tree_const_piterator
+    {
+      using __ptr_traits = pointer_traits<_ValPtr>;
+      using value_type = typename __ptr_traits::element_type;
+      using reference = const value_type&;
+      using pointer = const value_type*;
+
+      typedef _Rb_tree_piterator<_ValPtr>		iterator;
+      typedef typename iterator::_Base_ptr		_Base_ptr;
+
+      typedef bidirectional_iterator_tag iterator_category;
+      typedef ptrdiff_t			 difference_type;
+
+      typedef _Rb_tree_const_piterator			_Self;
+      typedef _Rb_tree_pnode_base<_ValPtr>		_Node_base;
+      typedef _Rb_tree_pnode<_ValPtr>			_Node_type;
+      typedef _Rb_tree_helpers<_Node_base>		_Tree_helpers;
+
+      _Rb_tree_const_piterator() _GLIBCXX_NOEXCEPT
+      : _M_node() { }
+
+      explicit
+      _Rb_tree_const_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      : _M_node(__x) { }
+
+      _Rb_tree_const_piterator(const iterator& __it) _GLIBCXX_NOEXCEPT
+      : _M_node(__it._M_node) { }
+
+      reference
+      operator*() const _GLIBCXX_NOEXCEPT
+      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
+
+      pointer
+      operator->() const _GLIBCXX_NOEXCEPT
+      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
+
+      _Self&
+      operator++() _GLIBCXX_NOEXCEPT
+      {
+	_M_node = _Tree_helpers::_Increment(_M_node);
+	return *this;
+      }
+
+      _Self
+      operator++(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	_M_node = _Tree_helpers::_Increment(_M_node);
+	return __tmp;
+      }
+
+      _Self&
+      operator--() _GLIBCXX_NOEXCEPT
+      {
+	_M_node = _Tree_helpers::_Decrement(_M_node);
+	return *this;
+      }
+
+      _Self
+      operator--(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	_M_node = _Tree_helpers::_Decrement(_M_node);
+	return __tmp;
+      }
+
+      friend bool
+      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node == __y._M_node; }
+
+#if ! __cpp_lib_three_way_comparison
+      friend bool
+      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node != __y._M_node; }
+#endif
+
+      _Base_ptr _M_node;
+    };
+#endif // C++11
+
+#if __cplusplus >= 201103L
+  template<typename _ValPtr>
+    struct _Rb_tree_node_traits
+    {
+      using _Node_base		= _Rb_tree_pnode_base<_ValPtr>;
+      using _Node_type		= _Rb_tree_pnode<_ValPtr>;
+      using _Header_t		= _Rb_tree_pheader<_Node_base>;
+      using _Iterator_t		= _Rb_tree_piterator<_ValPtr>;
+      using _Const_iterator_t	= _Rb_tree_const_piterator<_ValPtr>;
+    };
+#else
+  template<typename _ValPtr>
+    struct _Rb_tree_node_traits;
+#endif
+
+  template<typename _Val>
+    struct _Rb_tree_node_traits<_Val*>
+    {
+      typedef _Rb_tree_node_base		_Node_base;
+      typedef _Rb_tree_node<_Val>		_Node_type;
+      typedef _Rb_tree_header			_Header_t;
+      typedef _Rb_tree_iterator<_Val>		_Iterator_t;
+      typedef _Rb_tree_const_iterator<_Val>	_Const_iterator_t;
+    };
 
 #if __cplusplus > 201402L
   template<typename _Tree1, typename _Cmp2>
@@ -424,16 +1068,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc = allocator<_Val> >
     class _Rb_tree
     {
+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::pointer _ValPtr;
+      typedef _Rb_tree_node_traits<_ValPtr> _Node_traits;
+
+      typedef typename _Node_traits::_Node_base		_Node_base;
+      typedef typename _Node_traits::_Node_type		_Node_type;
+
       typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
-	rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
+	rebind<_Node_type>::other _Node_allocator;
 
       typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
 
     protected:
-      typedef _Rb_tree_node_base* 		_Base_ptr;
-      typedef const _Rb_tree_node_base* 	_Const_Base_ptr;
-      typedef _Rb_tree_node<_Val>* 		_Link_type;
-      typedef const _Rb_tree_node<_Val>*	_Const_Link_type;
+      typedef typename _Node_base::_Base_ptr 		_Base_ptr;
+      typedef typename _Node_type::_Node_ptr 		_Node_ptr;
+      typedef _Rb_tree_helpers<_Node_base>		_Tree_helpers;
 
     private:
       // Functor recycling a pool of nodes and using allocation once the pool
@@ -459,13 +1108,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
 	~_Reuse_or_alloc_node()
-	{ _M_t._M_erase(static_cast<_Link_type>(_M_root)); }
+	{ _M_t._M_erase(_M_root); }
 
 	template<typename _Arg>
-	  _Link_type
+	  _Base_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg)
 	  {
-	    _Link_type __node = static_cast<_Link_type>(_M_extract());
+	    _Base_ptr __node = _M_extract();
 	    if (__node)
 	      {
 		_M_t._M_destroy_node(__node);
@@ -524,7 +1173,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: _M_t(__t) { }
 
 	template<typename _Arg>
-	  _Link_type
+	  _Node_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg) const
 	  { return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg)); }
 
@@ -556,18 +1205,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return allocator_type(_M_get_Node_allocator()); }
 
     protected:
-      _Link_type
+      _Node_ptr
       _M_get_node()
       { return _Alloc_traits::allocate(_M_get_Node_allocator(), 1); }
 
       void
-      _M_put_node(_Link_type __p) _GLIBCXX_NOEXCEPT
-      { _Alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1); }
+      _M_put_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
+      {
+	_Alloc_traits::deallocate
+	  (_M_get_Node_allocator(), __p->template _M_node_ptr<_Node_ptr>(), 1);
+      }
 
 #if __cplusplus < 201103L
       void
-      _M_construct_node(_Link_type __node, const value_type& __x)
+      _M_construct_node(_Base_ptr __p, const value_type& __x)
       {
+	_Node_ptr __node = static_cast<_Node_ptr>(__p);
 	__try
 	  { get_allocator().construct(__node->_M_valptr(), __x); }
 	__catch(...)
@@ -577,79 +1230,82 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  }
       }
 
-      _Link_type
+      _Node_ptr
       _M_create_node(const value_type& __x)
       {
-	_Link_type __tmp = _M_get_node();
+	_Node_ptr __tmp = _M_get_node();
 	_M_construct_node(__tmp, __x);
 	return __tmp;
       }
 #else
       template<typename... _Args>
 	void
-	_M_construct_node(_Link_type __node, _Args&&... __args)
+	_M_construct_node(_Base_ptr __p, _Args&&... __args)
 	{
+	  _Node_type& __node = static_cast<_Node_type&>(*__p);
 	  __try
 	    {
-	      ::new(__node) _Rb_tree_node<_Val>;
+	      ::new(std::__addressof(__node)) _Node_type;
 	      _Alloc_traits::construct(_M_get_Node_allocator(),
-				       __node->_M_valptr(),
+				       __node._M_valptr(),
 				       std::forward<_Args>(__args)...);
 	    }
 	  __catch(...)
 	    {
-	      __node->~_Rb_tree_node<_Val>();
-	      _M_put_node(__node);
+	      __node.~_Node_type();
+	      _M_put_node(__p);
 	      __throw_exception_again;
 	    }
 	}
 
       template<typename... _Args>
-	_Link_type
+	_Node_ptr
 	_M_create_node(_Args&&... __args)
 	{
-	  _Link_type __tmp = _M_get_node();
+	  _Node_ptr __tmp = _M_get_node();
 	  _M_construct_node(__tmp, std::forward<_Args>(__args)...);
 	  return __tmp;
 	}
 #endif
 
       void
-      _M_destroy_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_destroy_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
       {
 #if __cplusplus < 201103L
-	get_allocator().destroy(__p->_M_valptr());
+	get_allocator().destroy(static_cast<_Node_ptr>(__p)->_M_valptr());
 #else
-	_Alloc_traits::destroy(_M_get_Node_allocator(), __p->_M_valptr());
-	__p->~_Rb_tree_node<_Val>();
+	_Node_type& __node = static_cast<_Node_type&>(*__p);
+	_Alloc_traits::destroy(_M_get_Node_allocator(), __node._M_valptr());
+	__node.~_Node_type();
 #endif
       }
 
       void
-      _M_drop_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_drop_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
       {
 	_M_destroy_node(__p);
 	_M_put_node(__p);
       }
 
       template<bool _MoveValue, typename _NodeGen>
-	_Link_type
-	_M_clone_node(_Link_type __x, _NodeGen& __node_gen)
+	_Base_ptr
+	_M_clone_node(_Base_ptr __x, _NodeGen& __node_gen)
 	{
 #if __cplusplus >= 201103L
 	  using _Vp = __conditional_t<_MoveValue,
 				      value_type&&,
 				      const value_type&>;
 #endif
-	  _Link_type __tmp
-	    = __node_gen(_GLIBCXX_FORWARD(_Vp, *__x->_M_valptr()));
+	  _Base_ptr __tmp = __node_gen
+	    (_GLIBCXX_FORWARD(_Vp, *static_cast<_Node_type&>(*__x)._M_valptr()));
 	  __tmp->_M_color = __x->_M_color;
-	  __tmp->_M_left = 0;
-	  __tmp->_M_right = 0;
+	  __tmp->_M_left = __tmp->_M_right = _Base_ptr();
 	  return __tmp;
 	}
 
     protected:
+      typedef typename _Node_traits::_Header_t		_Header_t;
+
 #if _GLIBCXX_INLINE_VERSION
       template<typename _Key_compare>
 #else
@@ -660,7 +1316,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	struct _Rb_tree_impl
 	: public _Node_allocator
 	, public _Rb_tree_key_compare<_Key_compare>
-	, public _Rb_tree_header
+	, public _Header_t
 	{
 	  typedef _Rb_tree_key_compare<_Key_compare> _Base_key_compare;
 
@@ -674,7 +1330,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Rb_tree_impl(const _Rb_tree_impl& __x)
 	  : _Node_allocator(_Alloc_traits::_S_select_on_copy(__x))
 	  , _Base_key_compare(__x._M_key_compare)
-	  , _Rb_tree_header()
+	  , _Header_t()
 	  { }
 
 #if __cplusplus < 201103L
@@ -694,7 +1350,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Rb_tree_impl(_Rb_tree_impl&& __x, _Node_allocator&& __a)
 	  : _Node_allocator(std::move(__a)),
 	    _Base_key_compare(std::move(__x)),
-	    _Rb_tree_header(std::move(__x))
+	    _Header_t(std::move(__x))
 	  { }
 
 	  _Rb_tree_impl(const _Key_compare& __comp, _Node_allocator&& __a)
@@ -710,7 +1366,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_root() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_parent; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_root() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_parent; }
 
@@ -718,7 +1374,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_leftmost() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_left; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_leftmost() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_left; }
 
@@ -726,35 +1382,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_rightmost() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_right; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_rightmost() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_right; }
 
-      _Link_type
-      _M_mbegin() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(this->_M_impl._M_header._M_parent); }
-
-      _Link_type
-      _M_begin() _GLIBCXX_NOEXCEPT
-      { return _M_mbegin(); }
-
-      _Const_Link_type
+      _Base_ptr
       _M_begin() const _GLIBCXX_NOEXCEPT
-      {
-	return static_cast<_Const_Link_type>
-	  (this->_M_impl._M_header._M_parent);
-      }
+      { return this->_M_impl._M_header._M_parent; }
 
       _Base_ptr
-      _M_end() _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
-
-      _Const_Base_ptr
       _M_end() const _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
+      { return this->_M_impl._M_header._M_self_ptr(); }
 
       static const _Key&
-      _S_key(_Const_Link_type __x)
+      _S_key(_Base_ptr __x)
       {
 #if __cplusplus >= 201103L
 	// If we're asking for the key we're presumably using the comparison
@@ -772,48 +1413,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 # endif // C++17
 #endif // C++11
 
-	return _KeyOfValue()(*__x->_M_valptr());
+	return _KeyOfValue()(*static_cast<const _Node_type&>(*__x)._M_valptr());
       }
 
-      static _Link_type
+      static _Base_ptr
       _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_left); }
+      { return __x->_M_left; }
 
-      static _Const_Link_type
-      _S_left(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_left); }
-
-      static _Link_type
+      static _Base_ptr
       _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_right); }
-
-      static _Const_Link_type
-      _S_right(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_right); }
-
-      static const _Key&
-      _S_key(_Const_Base_ptr __x)
-      { return _S_key(static_cast<_Const_Link_type>(__x)); }
+      { return __x->_M_right; }
 
       static _Base_ptr
       _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
-
-      static _Const_Base_ptr
-      _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
+      { return _Tree_helpers::_S_minimum(__x); }
 
       static _Base_ptr
       _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
-
-      static _Const_Base_ptr
-      _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
+      { return _Tree_helpers::_S_maximum(__x); }
 
     public:
-      typedef _Rb_tree_iterator<value_type>       iterator;
-      typedef _Rb_tree_const_iterator<value_type> const_iterator;
+      typedef typename _Node_traits::_Iterator_t	iterator;
+      typedef typename _Node_traits::_Const_iterator_t	const_iterator;
 
       typedef std::reverse_iterator<iterator>       reverse_iterator;
       typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
@@ -846,7 +1467,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_(_Base_ptr __x, _Base_ptr __y, _Arg&& __v, _NodeGen&);
 
       iterator
-      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Link_type __z);
+      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Base_ptr __z);
 
       template<typename _Arg>
 	iterator
@@ -857,10 +1478,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_equal_lower(_Arg&& __x);
 
       iterator
-      _M_insert_lower_node(_Base_ptr __p, _Link_type __z);
+      _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z);
 
       iterator
-      _M_insert_equal_lower_node(_Link_type __z);
+      _M_insert_equal_lower_node(_Base_ptr __z);
 #else
       template<typename _NodeGen>
 	iterator
@@ -879,22 +1500,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       enum { __as_lvalue, __as_rvalue };
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
-	_M_copy(_Link_type, _Base_ptr, _NodeGen&);
+	_Base_ptr
+	_M_copy(_Base_ptr, _Base_ptr, _NodeGen&);
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
+	_Base_ptr
 	_M_copy(const _Rb_tree& __x, _NodeGen& __gen)
 	{
-	  _Link_type __root =
-	    _M_copy<_MoveValues>(__x._M_mbegin(), _M_end(), __gen);
-	  _M_leftmost() = _S_minimum(__root);
-	  _M_rightmost() = _S_maximum(__root);
+	  _Base_ptr __root =
+	    _M_copy<_MoveValues>(__x._M_begin(), _M_end(), __gen);
+	  _M_leftmost() = _Tree_helpers::_S_minimum(__root);
+	  _M_rightmost() = _Tree_helpers::_S_maximum(__root);
 	  _M_impl._M_node_count = __x._M_impl._M_node_count;
 	  return __root;
 	}
 
-      _Link_type
+      _Base_ptr
       _M_copy(const _Rb_tree& __x)
       {
 	_Alloc_node __an(*this);
@@ -902,22 +1523,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       void
-      _M_erase(_Link_type __x);
-
-      iterator
-      _M_lower_bound(_Link_type __x, _Base_ptr __y,
-		     const _Key& __k);
+      _M_erase(_Base_ptr __x);
 
-      const_iterator
-      _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _Base_ptr
+      _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k) const;
 
-      iterator
-      _M_upper_bound(_Link_type __x, _Base_ptr __y,
-		     const _Key& __k);
-
-      const_iterator
-      _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _Base_ptr
+      _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k) const;
 
     public:
@@ -935,7 +1548,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x)
       : _M_impl(__x._M_impl)
       {
-	if (__x._M_root() != 0)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -947,7 +1560,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x, const allocator_type& __a)
       : _M_impl(__x._M_impl._M_key_compare, _Node_allocator(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -966,7 +1579,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a, false_type)
       : _M_impl(__x._M_impl._M_key_compare, std::move(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_move_data(__x, false_type{});
       }
 
@@ -1001,11 +1614,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       end() _GLIBCXX_NOEXCEPT
-      { return iterator(&this->_M_impl._M_header); }
+      { return iterator(this->_M_impl._M_header._M_self_ptr()); }
 
       const_iterator
       end() const _GLIBCXX_NOEXCEPT
-      { return const_iterator(&this->_M_impl._M_header); }
+      { return const_iterator(this->_M_impl._M_header._M_self_ptr()); }
 
       reverse_iterator
       rbegin() _GLIBCXX_NOEXCEPT
@@ -1194,7 +1807,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	const_iterator __result = __position;
 	++__result;
 	_M_erase_aux(__position);
-	return __result._M_const_cast();
+	return iterator(__result._M_node);
       }
 
       // LWG 2059.
@@ -1235,7 +1848,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       erase(const_iterator __first, const_iterator __last)
       {
 	_M_erase_aux(__first, __last);
-	return __last._M_const_cast();
+	return iterator(__last._M_node);
       }
 #else
       void
@@ -1266,19 +1879,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       lower_bound(const key_type& __k)
-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
+      { return iterator(_M_lower_bound(_M_begin(), _M_end(), __k)); }
 
       const_iterator
       lower_bound(const key_type& __k) const
-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
+      {
+	return const_iterator
+	  (_M_lower_bound(_M_begin(), _M_end(), __k));
+      }
 
       iterator
       upper_bound(const key_type& __k)
-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
+      { return iterator(_M_upper_bound(_M_begin(), _M_end(), __k)); }
 
       const_iterator
       upper_bound(const key_type& __k) const
-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
+      {
+	return const_iterator
+	  (_M_upper_bound(_M_begin(), _M_end(), __k));
+      }
 
       pair<iterator, iterator>
       equal_range(const key_type& __k);
@@ -1293,7 +1912,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_find_tr(const _Kt& __k)
 	{
 	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_find_tr(__k)._M_const_cast();
+	  return iterator(__const_this->_M_find_tr(__k)._M_node);
 	}
 
       template<typename _Kt,
@@ -1301,7 +1920,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	const_iterator
 	_M_find_tr(const _Kt& __k) const
 	{
-	  auto __j = _M_lower_bound_tr(__k);
+	  const_iterator __j(_M_lower_bound_tr(__k));
 	  if (__j != end() && _M_impl._M_key_compare(__k, _S_key(__j._M_node)))
 	    __j = end();
 	  return __j;
@@ -1318,16 +1937,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename _Kt,
 	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	iterator
-	_M_lower_bound_tr(const _Kt& __k)
-	{
-	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_lower_bound_tr(__k)._M_const_cast();
-	}
-
-      template<typename _Kt,
-	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	const_iterator
+	_Base_ptr
 	_M_lower_bound_tr(const _Kt& __k) const
 	{
 	  auto __x = _M_begin();
@@ -1340,21 +1950,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      }
 	    else
 	      __x = _S_right(__x);
-	  return const_iterator(__y);
-	}
-
-      template<typename _Kt,
-	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	iterator
-	_M_upper_bound_tr(const _Kt& __k)
-	{
-	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_upper_bound_tr(__k)._M_const_cast();
+	  return __y;
 	}
 
       template<typename _Kt,
 	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	const_iterator
+	_Base_ptr
 	_M_upper_bound_tr(const _Kt& __k) const
 	{
 	  auto __x = _M_begin();
@@ -1367,7 +1968,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      }
 	    else
 	      __x = _S_right(__x);
-	  return const_iterator(__y);
+	  return __y;
 	}
 
       template<typename _Kt,
@@ -1377,7 +1978,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  const _Rb_tree* __const_this = this;
 	  auto __ret = __const_this->_M_equal_range_tr(__k);
-	  return { __ret.first._M_const_cast(), __ret.second._M_const_cast() };
+	  return
+	    { iterator(__ret.first._M_node), iterator(__ret.second._M_node) };
 	}
 
       template<typename _Kt,
@@ -1385,7 +1987,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	pair<const_iterator, const_iterator>
 	_M_equal_range_tr(const _Kt& __k) const
 	{
-	  auto __low = _M_lower_bound_tr(__k);
+	  const_iterator __low(_M_lower_bound_tr(__k));
 	  auto __high = __low;
 	  auto& __cmp = _M_impl._M_key_compare;
 	  while (__high != end() && !__cmp(__k, _S_key(__high._M_node)))
@@ -1530,10 +2132,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       node_type
       extract(const_iterator __pos)
       {
-	auto __ptr = _Rb_tree_rebalance_for_erase(
-	    __pos._M_const_cast()._M_node, _M_impl._M_header);
+	auto __ptr = _Tree_helpers::_S_rebalance_for_erase
+	  (__pos._M_node, _M_impl._M_header);
 	--_M_impl._M_node_count;
-	return { static_cast<_Link_type>(__ptr), _M_get_Node_allocator() };
+	return
+	  { __ptr->template _M_node_ptr<_Node_ptr>(), _M_get_Node_allocator() };
       }
 
       /// Extract a node.
@@ -1567,11 +2170,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr = _Tree_helpers::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
-		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+		  _M_insert_node(__res.first, __res.second, __ptr);
 		}
 	    }
 	}
@@ -1589,11 +2191,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr = _Tree_helpers::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
-		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+		  _M_insert_node(__res.first, __res.second, __ptr);
 		}
 	    }
 	}
@@ -1666,7 +2267,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
 	_Rb_tree& _M_t;
-	_Link_type _M_node;
+	_Node_ptr _M_node;
       };
 #endif // C++11
     };
@@ -1704,7 +2305,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_move_assign(_Rb_tree& __x, true_type)
     {
       clear();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	_M_move_data(__x, true_type());
       std::__alloc_on_move(_M_get_Node_allocator(),
 			   __x._M_get_Node_allocator());
@@ -1723,7 +2324,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // structure.
       _Reuse_or_alloc_node __roan(*this);
       _M_impl._M_reset();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	{
 	  _M_root() = _M_copy<__as_rvalue>(__x, __roan);
 	  __x.clear();
@@ -1798,7 +2399,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Reuse_or_alloc_node __roan(*this);
 	  _M_impl._M_reset();
 	  _M_impl._M_key_compare = __x._M_impl._M_key_compare;
-	  if (__x._M_root() != 0)
+	  if (__x._M_root())
 	    _M_root() = _M_copy<__as_lvalue>(__x, __roan);
 	}
 
@@ -1826,10 +2427,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 			      || _M_impl._M_key_compare(_KeyOfValue()(__v),
 							_S_key(__p)));
 
-	_Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
+	_Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
 
-	_Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				      this->_M_impl._M_header);
+	_Tree_helpers::_S_insert_and_rebalance
+	  (__insert_left, __z, __p, this->_M_impl._M_header);
 	++_M_impl._M_node_count;
 	return iterator(__z);
       }
@@ -1851,10 +2452,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _KeyOfValue()(__v)));
 
-      _Link_type __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
+      _Base_ptr __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Tree_helpers::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -1872,7 +2473,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_insert_equal_lower(const _Val& __v)
 #endif
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       while (__x != 0)
 	{
@@ -1886,12 +2487,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Key, typename _Val, typename _KoV,
 	   typename _Compare, typename _Alloc>
     template<bool _MoveValues, typename _NodeGen>
-      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Link_type
+      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Base_ptr
       _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::
-      _M_copy(_Link_type __x, _Base_ptr __p, _NodeGen& __node_gen)
+      _M_copy(_Base_ptr __x, _Base_ptr __p, _NodeGen& __node_gen)
       {
 	// Structural copy. __x and __p must be non-null.
-	_Link_type __top = _M_clone_node<_MoveValues>(__x, __node_gen);
+	_Base_ptr __top = _M_clone_node<_MoveValues>(__x, __node_gen);
 	__top->_M_parent = __p;
 
 	__try
@@ -1904,7 +2505,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 	    while (__x != 0)
 	      {
-		_Link_type __y = _M_clone_node<_MoveValues>(__x, __node_gen);
+		_Base_ptr __y = _M_clone_node<_MoveValues>(__x, __node_gen);
 		__p->_M_left = __y;
 		__y->_M_parent = __p;
 		if (__x->_M_right)
@@ -1926,13 +2527,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     void
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_erase(_Link_type __x)
+    _M_erase(_Base_ptr __x)
     {
       // Erase without rebalancing.
       while (__x != 0)
 	{
 	  _M_erase(_S_right(__x));
-	  _Link_type __y = _S_left(__x);
+	  _Base_ptr __y = _S_left(__x);
 	  _M_drop_node(__x);
 	  __x = __y;
 	}
@@ -1941,65 +2542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::iterator
+		      _Compare, _Alloc>::_Base_ptr
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Link_type __x, _Base_ptr __y,
-		   const _Key& __k)
-    {
-      while (__x != 0)
-	if (!_M_impl._M_key_compare(_S_key(__x), __k))
-	  __y = __x, __x = _S_left(__x);
-	else
-	  __x = _S_right(__x);
-      return iterator(__y);
-    }
-
-  template<typename _Key, typename _Val, typename _KeyOfValue,
-	   typename _Compare, typename _Alloc>
-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::const_iterator
-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	  __y = __x, __x = _S_left(__x);
 	else
 	  __x = _S_right(__x);
-      return const_iterator(__y);
+      return __y;
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::iterator
+		      _Compare, _Alloc>::_Base_ptr
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Link_type __x, _Base_ptr __y,
-		   const _Key& __k)
-    {
-      while (__x != 0)
-	if (_M_impl._M_key_compare(__k, _S_key(__x)))
-	  __y = __x, __x = _S_left(__x);
-	else
-	  __x = _S_right(__x);
-      return iterator(__y);
-    }
-
-  template<typename _Key, typename _Val, typename _KeyOfValue,
-	   typename _Compare, typename _Alloc>
-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::const_iterator
-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	  __y = __x, __x = _S_left(__x);
 	else
 	  __x = _S_right(__x);
-      return const_iterator(__y);
+      return __y;
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2011,9 +2580,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k)
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
 	    __x = _S_right(__x);
@@ -2021,13 +2590,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Link_type __xu(__x);
+	      _Base_ptr __xu(__x);
 	      _Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
-	      return pair<iterator,
-			  iterator>(_M_lower_bound(__x, __y, __k),
-				    _M_upper_bound(__xu, __yu, __k));
+	      return make_pair(iterator(_M_lower_bound(__x, __y, __k)),
+			       iterator(_M_upper_bound(__xu, __yu, __k)));
 	    }
 	}
       return pair<iterator, iterator>(iterator(__y),
@@ -2043,8 +2611,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k) const
     {
-      _Const_Link_type __x = _M_begin();
-      _Const_Base_ptr __y = _M_end();
+      _Base_ptr __x = _M_begin();
+      _Base_ptr __y = _M_end();
       while (__x != 0)
 	{
 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
@@ -2053,13 +2621,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Const_Link_type __xu(__x);
-	      _Const_Base_ptr __yu(__y);
+	      _Base_ptr __xu(__x);
+	      _Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
-	      return pair<const_iterator,
-			  const_iterator>(_M_lower_bound(__x, __y, __k),
-					  _M_upper_bound(__xu, __yu, __k));
+	      return make_pair(const_iterator(_M_lower_bound(__x, __y, __k)),
+			       const_iterator(_M_upper_bound(__xu, __yu, __k)));
 	    }
 	}
       return pair<const_iterator, const_iterator>(const_iterator(__y),
@@ -2107,10 +2674,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_unique_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       bool __comp = true;
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __comp = _M_impl._M_key_compare(__k, _S_key(__x));
@@ -2139,9 +2706,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_equal_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = _M_impl._M_key_compare(__k, _S_key(__x)) ?
@@ -2209,11 +2776,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_hint_unique_pos(const_iterator __position,
 				  const key_type& __k)
     {
-      iterator __pos = __position._M_const_cast();
       typedef pair<_Base_ptr, _Base_ptr> _Res;
 
       // end()
-      if (__pos._M_node == _M_end())
+      if (__position._M_node == _M_end())
 	{
 	  if (size() > 0
 	      && _M_impl._M_key_compare(_S_key(_M_rightmost()), __k))
@@ -2221,32 +2787,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  else
 	    return _M_get_insert_unique_pos(__k);
 	}
-      else if (_M_impl._M_key_compare(__k, _S_key(__pos._M_node)))
+      else if (_M_impl._M_key_compare(__k, _S_key(__position._M_node)))
 	{
 	  // First, try before...
-	  iterator __before = __pos;
-	  if (__pos._M_node == _M_leftmost()) // begin()
+	  iterator __before(__position._M_node);
+	  if (__position._M_node == _M_leftmost()) // begin()
 	    return _Res(_M_leftmost(), _M_leftmost());
 	  else if (_M_impl._M_key_compare(_S_key((--__before)._M_node), __k))
 	    {
 	      if (_S_right(__before._M_node) == 0)
 		return _Res(0, __before._M_node);
 	      else
-		return _Res(__pos._M_node, __pos._M_node);
+		return _Res(__position._M_node, __position._M_node);
 	    }
 	  else
 	    return _M_get_insert_unique_pos(__k);
 	}
-      else if (_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
+      else if (_M_impl._M_key_compare(_S_key(__position._M_node), __k))
 	{
 	  // ... then try after.
-	  iterator __after = __pos;
-	  if (__pos._M_node == _M_rightmost())
+	  iterator __after(__position._M_node);
+	  if (__position._M_node == _M_rightmost())
 	    return _Res(0, _M_rightmost());
 	  else if (_M_impl._M_key_compare(__k, _S_key((++__after)._M_node)))
 	    {
-	      if (_S_right(__pos._M_node) == 0)
-		return _Res(0, __pos._M_node);
+	      if (_S_right(__position._M_node) == 0)
+		return _Res(0, __position._M_node);
 	      else
 		return _Res(__after._M_node, __after._M_node);
 	    }
@@ -2255,7 +2821,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
       else
 	// Equivalent keys.
-	return _Res(__pos._M_node, 0);
+	return _Res(__position._M_node, 0);
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2294,11 +2860,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
     {
-      iterator __pos = __position._M_const_cast();
       typedef pair<_Base_ptr, _Base_ptr> _Res;
 
       // end()
-      if (__pos._M_node == _M_end())
+      if (__position._M_node == _M_end())
 	{
 	  if (size() > 0
 	      && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
@@ -2306,18 +2871,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  else
 	    return _M_get_insert_equal_pos(__k);
 	}
-      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
+      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
 	{
 	  // First, try before...
 	  iterator __before = __pos;
-	  if (__pos._M_node == _M_leftmost()) // begin()
+	  if (__position._M_node == _M_leftmost()) // begin()
 	    return _Res(_M_leftmost(), _M_leftmost());
 	  else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
 	    {
 	      if (_S_right(__before._M_node) == 0)
 		return _Res(0, __before._M_node);
 	      else
-		return _Res(__pos._M_node, __pos._M_node);
+		return _Res(__position._M_node, __position._M_node);
 	    }
 	  else
 	    return _M_get_insert_equal_pos(__k);
@@ -2326,12 +2891,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  // ... then try after.
 	  iterator __after = __pos;
-	  if (__pos._M_node == _M_rightmost())
+	  if (__position._M_node == _M_rightmost())
 	    return _Res(0, _M_rightmost());
 	  else if (!_M_impl._M_key_compare(_S_key((++__after)._M_node), __k))
 	    {
-	      if (_S_right(__pos._M_node) == 0)
-		return _Res(0, __pos._M_node);
+	      if (_S_right(__position._M_node) == 0)
+		return _Res(0, __position._M_node);
 	      else
 		return _Res(__after._M_node, __after._M_node);
 	    }
@@ -2373,15 +2938,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Link_type __z)
+    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Base_ptr __z)
     -> iterator
     {
       bool __insert_left = (__x != 0 || __p == _M_end()
 			    || _M_impl._M_key_compare(_S_key(__z),
 						      _S_key(__p)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Tree_helpers::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -2390,15 +2955,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_lower_node(_Base_ptr __p, _Link_type __z)
+    _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z)
     -> iterator
     {
       bool __insert_left = (__p == _M_end()
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _S_key(__z)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Tree_helpers::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -2407,10 +2972,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_equal_lower_node(_Link_type __z)
+    _M_insert_equal_lower_node(_Base_ptr __z)
     -> iterator
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       while (__x != 0)
 	{
@@ -2487,10 +3052,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     _M_erase_aux(const_iterator __position)
     {
-      _Link_type __y =
-	static_cast<_Link_type>(_Rb_tree_rebalance_for_erase
-				(const_cast<_Base_ptr>(__position._M_node),
-				 this->_M_impl._M_header));
+      _Base_ptr __y = _Tree_helpers::_S_rebalance_for_erase
+	(__position._M_node, this->_M_impl._M_header);
       _M_drop_node(__y);
       --_M_impl._M_node_count;
     }
@@ -2527,7 +3090,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     find(const _Key& __k)
     {
-      iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
+      iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
       return (__j == end()
 	      || _M_impl._M_key_compare(__k,
 					_S_key(__j._M_node))) ? end() : __j;
@@ -2540,7 +3103,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     find(const _Key& __k) const
     {
-      const_iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
+      const_iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
       return (__j == end()
 	      || _M_impl._M_key_compare(__k,
 					_S_key(__j._M_node))) ? end() : __j;
@@ -2574,9 +3137,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       unsigned int __len = _Rb_tree_black_count(_M_leftmost(), _M_root());
       for (const_iterator __it = begin(); __it != end(); ++__it)
 	{
-	  _Const_Link_type __x = static_cast<_Const_Link_type>(__it._M_node);
-	  _Const_Link_type __L = _S_left(__x);
-	  _Const_Link_type __R = _S_right(__x);
+	  _Node_ptr __x = static_cast<_Node_ptr>(__it._M_node);
+	  _Node_ptr __L = _S_left(__x);
+	  _Node_ptr __R = _S_right(__x);
 
 	  if (__x->_M_color == _S_red)
 	    if ((__L && __L->_M_color == _S_red)
@@ -2592,9 +3155,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    return false;
 	}
 
-      if (_M_leftmost() != _Rb_tree_node_base::_S_minimum(_M_root()))
+      if (_M_leftmost() != _Tree_helpers::_S_minimum(_M_root()))
 	return false;
-      if (_M_rightmost() != _Rb_tree_node_base::_S_maximum(_M_root()))
+      if (_M_rightmost() != _Tree_helpers::_S_maximum(_M_root()))
 	return false;
       return true;
     }
diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..758beedccfd
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
@@ -0,0 +1,44 @@
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct H
+{
+  std::size_t
+  operator()(const T& t) const noexcept
+  { return t.i; }
+};
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::map<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::map<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( v.begin()->second == 0 );
+  VERIFY( ++v.begin() == v.end() );
+  v.insert({ T { 1 }, 1 });
+  VERIFY( v.size() == 2 );
+  VERIFY( v.find(T()) != v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..1a9e198531f
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
@@ -0,0 +1,40 @@
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct H
+{
+  std::size_t
+  operator()(const T& t) const noexcept
+  { return t.i; }
+};
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multimap<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::multimap<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..d4796e37672
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
@@ -0,0 +1,39 @@
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct H
+{
+  std::size_t
+  operator()(const T& t) const noexcept
+  { return t.i; }
+};
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multiset<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::multiset<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..1cf479083eb
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
@@ -0,0 +1,39 @@
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct H
+{
+  std::size_t
+  operator()(const T& t) const noexcept
+  { return t.i; }
+};
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::set<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::set<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
  
Jonathan Wakely Nov. 18, 2024, 4:39 p.m. UTC | #13
On 18/11/24 06:57 +0100, François Dumont wrote:
>Hi
>
>Here is a new proposal with all the cleanup regarding _Const_Base_ptr
>that makes support of allocator's fancy pointer type simpler.
>
>Also submitted as PR:
>
>https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
>
>   libstdc++: Add fancy pointer support in map and set
>
>    Support fancy allocator pointer type in std::_Rb_tree<>.
>
>    In case of fancy pointer type the container is now storing the
>pointer to
>    _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.
>
>    Many methods are adapted to take and return _Base_ptr in place of
>_Link_type
>    which has been renamed into _Node_ptr.
>
>    As all node are stored as _Base_ptr have all methods working with
>this type
>    and remove _Const_Base_ptr and all methods associated to it.
>
>    libstdc++-v3/ChangeLog:
>
>            * include/bits/stl_set.h (std::set<>::upper_bound<>(const
>_Kt&) const): Fix
>            decltype typo to use const_iterator.
>            * include/bits/stl_tree.h
>            (_Rb_tree_ptr_traits<>): New.
>            (_Rb_tree_pnode_base<>): New.
>            (_Rb_tree_node_base): Inherit from latter.
>            (_Rb_tree_node_base::_Const_Base_ptr): Remove.
>            (_Rb_tree_node_base::_S_minimum(_Const_Base_ptr)): Remove.
>            (_Rb_tree_node_base::_S_maximum(_Const_Base_ptr)): Remove.
>            (_Rb_tree_pheader): New.
>            (_Rb_tree_header): Inherit from latter.
>            (_Rb_tree_node_val): New.
>            (_Rb_tree_node): Inherit from latter.
>            (_Rb_tree_pnode): New.
>            (_Rb_tree_iterator<>::_Link_type): Rename into...
>            (_Rb_tree_iterator<>::_Node_ptr): ...this.
>            (_Rb_tree_const_iterator<>::_Link_type): Rename into...
>            (_Rb_tree_const_iterator<>::_Node_ptr): ...this.
>            (_Rb_tree_const_iterator<>::_M_node): Change type into
>_Base_ptr.
>            (_Rb_tree_const_iterator<>::_M_const_cast): Remove.
>            (_Rb_tree_helpers<>): New.
>            (_Rb_tree_piterator): New.
>            (_Rb_tree_const_piterator): New.
>            (_Rb_tree_node_traits<>): New.
>            (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
>            (_Rb_tree<>::_Const_Base_ptr): Remove.
>            (_Rb_tree): Adapt to generalize usage of _Base_ptr in
>place of _Link_type.
>            (_Rb_tree<>::_M_mbegin): Remove.
>            (_Rb_tree<>::_S_left(_Const_Base_ptr)): Remove.
>            (_Rb_tree<>::_S_right(_Const_Base_ptr)): Remove.
>            (_Rb_tree<>::_S_maximum(_Const_Base_ptr)): Remove.
>            (_Rb_tree<>::_S_minimum(_Const_Base_ptr)): Remove.
>            * testsuite/23_containers/map/allocator/ext_ptr.cc: New
>test case.
>            * testsuite/23_containers/multimap/allocator/ext_ptr.cc:
>New test case.
>            * testsuite/23_containers/multiset/allocator/ext_ptr.cc:
>New test case.
>            * testsuite/23_containers/set/allocator/ext_ptr.cc: New
>test case.
>
>Tested under Linux x64.
>
>
>Note that I've also run the 23_containers tests on map, multimap,
>multiset and set tweaking implementation
>so that new types are being used when C++11 or later even if allocator
>pointer type is a C pointer.

Please take a look at the _GLIBCXX_USE_ALLOC_PTR_xxx macros in
https://gcc.gnu.org/pipermail/gcc-patches/2024-November/669128.html
and https://gcc.gnu.org/pipermail/gcc-patches/2024-November/669127.html
Those macros allow the fancy pointer support to be disabled, in case
it will break anything for current users. Maybe more importantly, it
allows the new node types to be used always, even for T*. That means
we can run the entire testsuite using the new nodes without tweaking
the implementation.

Please consider a _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE macro (or some
other name).

>Ok to commit ?
>
>François
>
>On 12/11/2024 15:56, Jonathan Wakely wrote:
>>On Mon, 4 Nov 2024 at 21:34, François Dumont <frs.dumont@gmail.com> wrote:
>>>
>>>On 04/11/2024 19:45, Jonathan Wakely wrote:
>>>>On Mon, 4 Nov 2024 at 18:30, François Dumont <frs.dumont@gmail.com> wrote:
>>>>>On 21/10/2024 06:56, François Dumont wrote:
>>>>>
>>>>>
>>>>>On 17/10/2024 23:11, Jonathan Wakely wrote:
>>>>>
>>>>>
>>>>>
>>>>>On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
>>>>>>
>>>>>>On Thu, 17 Oct 2024 at 20:52, François Dumont <frs.dumont@gmail.com> wrote:
>>>>>>>Here is an updated version that compiles, I think, all your feedbacks. It's much cleaner indeed.
>>>>>>Thanks, I'll take a look tomorrow.
>>>>>>
>>>>>>>It's also tested in C++98/17/23.
>>>>>>>
>>>>>>>I'm surprised that we do not need to consider potential allocator::const_pointer.
>>>>>>Do you mean consider the case where Alloc::const_pointer is not the same type as rebinding 'pointer' to a const element type?
>>>>>Yes, exactly.
>>>>>
>>>>>
>>>>>>We don't need to consider that because we never get a 'const_pointer' from the allocator, and we never need to pass a 'const_pointer' to the allocator. The allocator's 'allocate' and 'deallocate' members both work with the 'pointer' type, so we only need to use that type when interacting with the allocator. For all the other uses, such as _Const_Node_ptr, what we need is a pointer-to-const that's compatible with the allocator's pointer type. It doesn't actually matter if it's the same type as allocator_traits<Alloc>::const_pointer, because we don't need
>>>>>Sorry, I sent the email before finishing that thought!
>>>>>
>>>>>... we don't need to pass a const_pointer to anything, we only need it for the container's own purposes.
>>>>>
>>>>>But thinking about it some more, do we even need a const-pointer for the container?  Currently the const_iterator stores a const-pointer, and some members like _M_root() and _M_leftmost() return a const-pointer. But they don't need to. The nodes are all pointed to by a non-const _Base_ptr, none of the storage managed by the container is const.
>>>>>
>>>>>Except _M_impl._M_header in a const context.
>>>>>
>>>>>Using const_cast would result in a similar UB that you fixed on _GLIBCXX_DEBUG containers, no ?
>>>>We only use a pointer to _M_header for the past-the-end iterator,
>>>>which is not dereferenceable. It's OK to do the const_cast, it's not
>>>>OK to write something through that pointer/reference if the object is
>>>>really const. But you can't write through a past-the-end pointer
>>>>anyway.
>>>>
>>>One last question then to complete this work, is it fine to change
>>>_Rb_tree_const_iterator::_M_node to _Base_ptr ? No abi issue in doing so ?
>>I thought I'd sent a reply to this question, but I don't see it in the
>>archives, sorry.
>>
>>So changing from _Const_Base_ptr to _Base_ptr? Yes, I think that's OK.
>>

>diff --git a/libstdc++-v3/include/bits/stl_set.h b/libstdc++-v3/include/bits/stl_set.h
>index c0eb4dbf65f..3d17626f330 100644
>--- a/libstdc++-v3/include/bits/stl_set.h
>+++ b/libstdc++-v3/include/bits/stl_set.h
>@@ -875,7 +875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>       template<typename _Kt>
>     auto
>     upper_bound(const _Kt& __x) const
>-    -> decltype(iterator(_M_t._M_upper_bound_tr(__x)))
>+    -> decltype(const_iterator(_M_t._M_upper_bound_tr(__x)))
>     { return const_iterator(_M_t._M_upper_bound_tr(__x)); }
> #endif
>       ///@}
>diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
>index bc27e191e8b..c6cba99726d 100644
>--- a/libstdc++-v3/include/bits/stl_tree.h
>+++ b/libstdc++-v3/include/bits/stl_tree.h
>@@ -96,44 +96,100 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>   enum _Rb_tree_color { _S_red = false, _S_black = true };
>
>-  struct _Rb_tree_node_base
>-  {
>-    typedef _Rb_tree_node_base* _Base_ptr;
>-    typedef const _Rb_tree_node_base* _Const_Base_ptr;
>-
>-    _Rb_tree_color    _M_color;
>-    _Base_ptr        _M_parent;
>-    _Base_ptr        _M_left;
>-    _Base_ptr        _M_right;
>-
>-    static _Base_ptr
>-    _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>+#if __cplusplus >= 201103L
>+  template<typename _Ptr>
>+    struct _Rb_tree_ptr_traits
>     {
>-      while (__x->_M_left != 0) __x = __x->_M_left;
>-      return __x;
>-    }
>+      template<typename _Tp>
>+    struct __rebind
>+    { using __type = __ptr_rebind<_Ptr, _Tp>; };

This only seems to be used in one place, can you just use __ptr_rebind
directly in that one place?

>+
>+      template<typename _BasePtr>
>+    static _Ptr
>+    _S_ptr_to(_BasePtr __this) noexcept
>+    { return pointer_traits<_Ptr>::pointer_to(*__this); }

This is also only needed in exactly one place. I avoided needing this
function by adding _M_base_ptr() to the node base classes.

The whole of this _Rb_tree_ptr_traits class template seems unnecessary
to me. Just inline the code directly into the places it's needed.
You're not optimizing anything by replacing
pointer_traits<T*>::pointer_to with
_Rb_tree_ptr_traits<T*>::_S_ptr_to, it's still a class template
specialization and a function call.

>+
>+      template<typename _BasePtr, typename _UpPtr>
>+    static _UpPtr
>+    _S_up_ptr_to(_BasePtr __this) noexcept

pointer_to is not guaranteed to be noexcept.

This appears to be a downcast, i.e. a base-to-derived cast, having
"up" in the name is a bit confusing.

>+    {
>+      using __ptr_traits = pointer_traits<_UpPtr>;
>+      using __up_type = typename __ptr_traits::element_type;
>+      auto __up_this = static_cast<__up_type*>(__this);
>+      return __ptr_traits::pointer_to(*__up_this);
>+    }
>+    };
>+#else
>+  template<typename _Ptr>
>+    struct _Rb_tree_ptr_traits;
>+#endif
>
>-    static _Const_Base_ptr
>-    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>+  template<typename _Tp>
>+    struct _Rb_tree_ptr_traits<_Tp*>
>     {
>-      while (__x->_M_left != 0) __x = __x->_M_left;
>-      return __x;
>-    }
>+      template<typename>
>+    struct __rebind
>+    { typedef _Tp* __type; };
>+
>+      template<typename _BasePtr>
>+    static _Tp*
>+    _S_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
>+    { return static_cast<_Tp*>(__this); }
>+
>+      template<typename _BasePtr, typename _UpPtr>
>+    static _UpPtr
>+    _S_up_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
>+    { return static_cast<_UpPtr>(__this); }
>+    };
>
>-    static _Base_ptr
>-    _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>+  template<typename _BasePtr>
>+    struct _Rb_tree_pnode_base
>     {
>-      while (__x->_M_right != 0) __x = __x->_M_right;
>-      return __x;
>-    }
>+      typedef typename _Rb_tree_ptr_traits<_BasePtr>::template
>+    __rebind<_Rb_tree_pnode_base>::__type _Base_ptr;

The names _BasePtr and _Base_ptr are very similar, and confusing.
It looks like sometimes the template argument that is substituted for
_BasePtr is sometimes a derived class (_Rb_tree_node_base) and
sometimes is _ValPtr. It's never a "base" pointer. Please rename it.

>
>-    static _Const_Base_ptr
>-    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>-    {
>-      while (__x->_M_right != 0) __x = __x->_M_right;
>-      return __x;
>-    }
>-  };
>+      _Base_ptr
>+      _M_self_ptr() _GLIBCXX_NOEXCEPT

Why do we need a non-const overload? The const one will work for all
cases, right?

I added a comment to my list patches pointing out that it's not
const-correct, but is safe as long as only used carefully.


>+      { return _Rb_tree_ptr_traits<_Base_ptr>::_S_ptr_to(this); }
>+
>+      _Base_ptr
>+      _M_self_ptr() const _GLIBCXX_NOEXCEPT
>+      {
>+    _Rb_tree_pnode_base* __self = const_cast<_Rb_tree_pnode_base*>(this);
>+    return __self->_M_self_ptr();
>+      }
>+
>+      template<typename _NodePtr>
>+    _NodePtr
>+    _M_node_ptr() _GLIBCXX_NOEXCEPT
>+    {
>+      return _Rb_tree_ptr_traits<_Base_ptr>::template
>+        _S_up_ptr_to<_Rb_tree_pnode_base*, _NodePtr>(this);
>+    }
>+
>+      _Rb_tree_color    _M_color;
>+      _Base_ptr        _M_parent;
>+      _Base_ptr        _M_left;
>+      _Base_ptr        _M_right;
>+
>+      static _Base_ptr
>+      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>+      {
>+    while (__x->_M_left) __x = __x->_M_left;
>+    return __x;
>+      }
>+
>+      static _Base_ptr
>+      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>+      {
>+    while (__x->_M_right) __x = __x->_M_right;
>+    return __x;
>+      }
>+    };
>+
>+  struct _Rb_tree_node_base
>+    : _Rb_tree_pnode_base<_Rb_tree_node_base*>
>+  { };

This approaches means that the existing specializations of RB trees
all get twice as many base classes. I don't see an ABI problem, and it
should generate the same code for optimized builds, but increasing the
inheritance depth and the number of class template instantiations
gives the compiler more work to do, compiling slower, and generating
additional debug info (and maybe RTTI?)

Please take a look at how I did it for the linked lists, where
specializations for non-fancy pointers, e.g. _List_node<T>, are mostly
unchanged. They don't get any new base classes, just some new typedefs
and new accessors like:

_Base_ptr _M_base() { return this; }
const _List_node* _M_base() const { return this; }

The _List_node_traits class template is used by std::list etc. to
select whether to use _List_node<Alloc::value_type> or
__list::_Node<Alloc::pointer>. All the fancy pointer support is in the
new type hierarchy, __list::_Node_base, __list::_Node, and
__list::_Iterator. The old types like _List_node don't really change.

I found that using the __list namespace with new types reduced a lot
of confusion I had between the very similar names _List_node and
_List_pnode (or _List_ptr_node, or other names like that).

Maybe we could call it _List::_Node instead of __list::_Node, so it's
a bit closer to _List_node but still distinguishable (and would
produce very slightly shorter mangled names). I didn't do that,
because our convention is to name impl namespaces as __foo not _Foo.

That pattern could naturally be extended to __tree::_Node (or
_Tree::_Node) as well.

>
>   // Helper type offering value initialization guarantee on the compare functor.
>   template<typename _Key_compare>
>@@ -163,81 +219,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     };
>
>   // Helper type to manage default initialization of node count and header.
>-  struct _Rb_tree_header
>-  {
>-    _Rb_tree_node_base    _M_header;
>-    size_t        _M_node_count; // Keeps track of size of tree.
>-
>-    _Rb_tree_header() _GLIBCXX_NOEXCEPT
>+  template<typename _NodeBase>
>+    struct _Rb_tree_pheader
>     {
>-      _M_header._M_color = _S_red;
>-      _M_reset();
>-    }
>+    private:
>+      typedef typename _NodeBase::_Base_ptr        _Base_ptr;
>+
>+    public:
>+      _NodeBase        _M_header;
>+      size_t        _M_node_count; // Keeps track of size of tree.
>+
>+      _Rb_tree_pheader() _GLIBCXX_NOEXCEPT
>+      {
>+    _M_header._M_color = _S_red;
>+    _M_reset();
>+      }
>
> #if __cplusplus >= 201103L
>-    _Rb_tree_header(_Rb_tree_header&& __x) noexcept
>-    {
>-      if (__x._M_header._M_parent != nullptr)
>-    _M_move_data(__x);
>-      else
>-    {
>-      _M_header._M_color = _S_red;
>-      _M_reset();
>-    }
>-    }
>+      _Rb_tree_pheader(_Rb_tree_pheader&& __x) noexcept
>+      {
>+    if (__x._M_header._M_parent)
>+      _M_move_data(__x);
>+    else
>+      {
>+        _M_header._M_color = _S_red;
>+        _M_reset();
>+      }
>+      }
> #endif
>
>-    void
>-    _M_move_data(_Rb_tree_header& __from)
>-    {
>-      _M_header._M_color = __from._M_header._M_color;
>-      _M_header._M_parent = __from._M_header._M_parent;
>-      _M_header._M_left = __from._M_header._M_left;
>-      _M_header._M_right = __from._M_header._M_right;
>-      _M_header._M_parent->_M_parent = &_M_header;
>-      _M_node_count = __from._M_node_count;
>-
>-      __from._M_reset();
>-    }
>+      void
>+      _M_move_data(_Rb_tree_pheader& __from)
>+      {
>+    _M_header._M_color = __from._M_header._M_color;
>+    _M_header._M_parent = __from._M_header._M_parent;
>+    _M_header._M_left = __from._M_header._M_left;
>+    _M_header._M_right = __from._M_header._M_right;
>+    _M_header._M_parent->_M_parent = _M_header._M_self_ptr();
>+    _M_node_count = __from._M_node_count;
>+
>+    __from._M_reset();
>+      }
>
>-    void
>-    _M_reset()
>-    {
>-      _M_header._M_parent = 0;
>-      _M_header._M_left = &_M_header;
>-      _M_header._M_right = &_M_header;
>-      _M_node_count = 0;
>-    }
>-  };
>+      void
>+      _M_reset()
>+      {
>+    _M_header._M_parent = _Base_ptr();
>+    _M_header._M_left = _M_header._M_right = _M_header._M_self_ptr();
>+    _M_node_count = 0;
>+      }
>+    };
>+
>+  struct _Rb_tree_header : _Rb_tree_pheader<_Rb_tree_node_base>
>+  { };
>
>   template<typename _Val>
>-    struct _Rb_tree_node : public _Rb_tree_node_base
>+    struct _Rb_tree_node_val
>     {
>-      typedef _Rb_tree_node<_Val>* _Link_type;
>-
> #if __cplusplus < 201103L
>       _Val _M_value_field;
>
>+      __attribute__((__always_inline__))
>       _Val*
>       _M_valptr()
>       { return std::__addressof(_M_value_field); }
>
>+      __attribute__((__always_inline__))
>       const _Val*
>       _M_valptr() const
>       { return std::__addressof(_M_value_field); }
> #else
>       __gnu_cxx::__aligned_membuf<_Val> _M_storage;

For the new __list::_Node type I took the opportunity to replace
__aligned_membuf with a union { _Val _M_data; }; which is simpler and
produces the same code. We could probably make that change for the
existing node types, I think it's safe, but making it only for the new
_Node type means the old node type isn't affected.


>+      [[__gnu__::__always_inline__]]
>       _Val*
>       _M_valptr()
>       { return _M_storage._M_ptr(); }
>
>+      [[__gnu__::__always_inline__]]
>       const _Val*
>       _M_valptr() const
>       { return _M_storage._M_ptr(); }
> #endif
>     };
>
>+  template<typename _Val>
>+    struct _Rb_tree_node
>+    : _Rb_tree_node_base
>+    , _Rb_tree_node_val<_Val>
>+    {
>+      typedef _Rb_tree_node<_Val>* _Node_ptr;
>+    };
>+
>+#if __cplusplus >= 201103L
>+  template<typename _ValPtr>
>+    struct _Rb_tree_pnode
>+    : _Rb_tree_pnode_base<_ValPtr>

This makes _Rb_tree_pnode_base depend on the value_type, which means
you instantiate a different specialization of _Rb_tree_pnode_base for
every _Rb_tree. There's no point even having a common base class if
every specialization is different. You might as well just put the
_M_parent/_M_left/_M_right pointers in _Rb_tree_pnode directly.

The point of the node_base class is to have a common base that is not
dependent on the value type, so that std::set<int> and std::set<long>
and std::set<int, Cmp> all use the same node base. The way this patch
works, std::set<int> will use _Rb_tree_pnode_base<int*> and
std::set<long> will use _Rb_tree_pnode_base<long*>.

This is why my patch for lists uses __ptr_rebind<_ValPtr, void> as the
template argument to _Node_base, so that they all use
_Node_base<void*>.

Obviously for a fancy pointer, it will be a different specialization,
so std::list<int, A<int>> uses _Node_base<A::void_pointer>, but
std::list<long, A<long>> uses the same _Node_base<A::void_pointer>.

>+    , _Rb_tree_node_val<typename std::pointer_traits<_ValPtr>::element_type>
>+    {
>+      using _Node_ptr = __ptr_rebind<_ValPtr, _Rb_tree_pnode>;
>+    };
>+#endif
>+
>   _GLIBCXX_PURE _Rb_tree_node_base*
>   _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
>

[...]

>+#if __cplusplus >= 201103L
>+  template<typename _ValPtr>
>+    struct _Rb_tree_piterator
>+    {
>+      using __ptr_traits = pointer_traits<_ValPtr>;
>+      using value_type = typename __ptr_traits::element_type;
>+      using reference = value_type&;
>+      using pointer = value_type*;
>+
>+      typedef bidirectional_iterator_tag iterator_category;
>+      typedef ptrdiff_t             difference_type;
>+
>+      typedef _Rb_tree_piterator            _Self;
>+      typedef _Rb_tree_pnode_base<_ValPtr>        _Node_base;
>+      typedef _Rb_tree_pnode<_ValPtr>            _Node_type;
>+      typedef typename _Node_type::_Base_ptr        _Base_ptr;
>+      typedef typename _Node_type::_Node_ptr        _Node_ptr;
>+      typedef _Rb_tree_helpers<_Node_base>        _Tree_helpers;
>+
>+      _Rb_tree_piterator() _GLIBCXX_NOEXCEPT
>+      : _M_node() { }
>+
>+      explicit
>+      _Rb_tree_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>+      : _M_node(__x) { }
>+
>+      reference
>+      operator*() const _GLIBCXX_NOEXCEPT
>+      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>+
>+      pointer
>+      operator->() const _GLIBCXX_NOEXCEPT
>+      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>+
>+      _Self&
>+      operator++() _GLIBCXX_NOEXCEPT
>+      {
>+    _M_node = _Tree_helpers::_Increment(_M_node);
>+    return *this;
>+      }
>+
>+      _Self
>+      operator++(int) _GLIBCXX_NOEXCEPT
>+      {
>+    _Self __tmp = *this;
>+    _M_node = _Tree_helpers::_Increment(_M_node);
>+    return __tmp;
>+      }
>+
>+      _Self&
>+      operator--() _GLIBCXX_NOEXCEPT
>+      {
>+    _M_node = _Tree_helpers::_Decrement(_M_node);
>+    return *this;
>+      }
>+
>+      _Self
>+      operator--(int) _GLIBCXX_NOEXCEPT
>+      {
>+    _Self __tmp = *this;
>+    _M_node = _Tree_helpers::_Decrement(_M_node);
>+    return __tmp;
>+      }
>+
>+      friend bool
>+      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>+      { return __x._M_node == __y._M_node; }
>+
>+#if ! __cpp_lib_three_way_comparison
>+      friend bool
>+      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>+      { return __x._M_node != __y._M_node; }
>+#endif
>+
>+      _Base_ptr _M_node;
>+    };
>+
>+  template<typename _ValPtr>
>+    struct _Rb_tree_const_piterator

For the linked lists, I didn't add two new iterator types, just
_Iterator<_Const, _ValPtr> which is used for iterator and
const_iterator.

It seemed nice not to duplicate the iterator type, since the iterator
and const_iterator are 95% the same. If we don't need to support
C++98, we can write a single iterator implementation.

If you have a single iterator implementation, you probably don't need
the _Rb_tree_helpers.

There would still be uses of insert-and-rebalance and
rebalance-for-erase, but those could just be new overloads for fancy
pointers. I don't think we need the _Rb_tree_helpers class template.


>+    {
>+      using __ptr_traits = pointer_traits<_ValPtr>;
>+      using value_type = typename __ptr_traits::element_type;
>+      using reference = const value_type&;
>+      using pointer = const value_type*;
>+
>+      typedef _Rb_tree_piterator<_ValPtr>        iterator;
>+      typedef typename iterator::_Base_ptr        _Base_ptr;
>+
>+      typedef bidirectional_iterator_tag iterator_category;
>+      typedef ptrdiff_t             difference_type;
>+
>+      typedef _Rb_tree_const_piterator            _Self;
>+      typedef _Rb_tree_pnode_base<_ValPtr>        _Node_base;
>+      typedef _Rb_tree_pnode<_ValPtr>            _Node_type;
>+      typedef _Rb_tree_helpers<_Node_base>        _Tree_helpers;
>+
>+      _Rb_tree_const_piterator() _GLIBCXX_NOEXCEPT
>+      : _M_node() { }
>+
>+      explicit
>+      _Rb_tree_const_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>+      : _M_node(__x) { }
>+
>+      _Rb_tree_const_piterator(const iterator& __it) _GLIBCXX_NOEXCEPT
>+      : _M_node(__it._M_node) { }
>+
>+      reference
>+      operator*() const _GLIBCXX_NOEXCEPT
>+      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>+
>+      pointer
>+      operator->() const _GLIBCXX_NOEXCEPT
>+      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>+
>+      _Self&
>+      operator++() _GLIBCXX_NOEXCEPT
>+      {
>+    _M_node = _Tree_helpers::_Increment(_M_node);

You need the iterator increment code here, but you can define it
inline without the _Increment helper.

>+    return *this;
>+      }
>+
>+      _Self
>+      operator++(int) _GLIBCXX_NOEXCEPT
>+      {
>+    _Self __tmp = *this;
>+    _M_node = _Tree_helpers::_Increment(_M_node);

This could just use ++*this. So if there's no separate const iterator
implementation, there's no more use of _Increment.

>+    return __tmp;
>+      }
>+
>+      _Self&
>+      operator--() _GLIBCXX_NOEXCEPT
>+      {
>+    _M_node = _Tree_helpers::_Decrement(_M_node);
>+    return *this;
>+      }
>+
>+      _Self
>+      operator--(int) _GLIBCXX_NOEXCEPT
>+      {
>+    _Self __tmp = *this;
>+    _M_node = _Tree_helpers::_Decrement(_M_node);
>+    return __tmp;
>+      }
>+
>+      friend bool
>+      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>+      { return __x._M_node == __y._M_node; }
>+
>+#if ! __cpp_lib_three_way_comparison
>+      friend bool
>+      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>+      { return __x._M_node != __y._M_node; }
>+#endif
>+
>+      _Base_ptr _M_node;
>+    };
>+#endif // C++11
>+
>+#if __cplusplus >= 201103L
>+  template<typename _ValPtr>
>+    struct _Rb_tree_node_traits
>+    {
>+      using _Node_base        = _Rb_tree_pnode_base<_ValPtr>;
>+      using _Node_type        = _Rb_tree_pnode<_ValPtr>;
>+      using _Header_t        = _Rb_tree_pheader<_Node_base>;
>+      using _Iterator_t        = _Rb_tree_piterator<_ValPtr>;
>+      using _Const_iterator_t    = _Rb_tree_const_piterator<_ValPtr>;
>+    };
>+#else
>+  template<typename _ValPtr>
>+    struct _Rb_tree_node_traits;
>+#endif
>+
>+  template<typename _Val>
>+    struct _Rb_tree_node_traits<_Val*>
>+    {
>+      typedef _Rb_tree_node_base        _Node_base;
>+      typedef _Rb_tree_node<_Val>        _Node_type;
>+      typedef _Rb_tree_header            _Header_t;
>+      typedef _Rb_tree_iterator<_Val>        _Iterator_t;
>+      typedef _Rb_tree_const_iterator<_Val>    _Const_iterator_t;
>+    };
>
> #if __cplusplus > 201402L
>   template<typename _Tree1, typename _Cmp2>
>@@ -424,16 +1068,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        typename _Compare, typename _Alloc = allocator<_Val> >
>     class _Rb_tree
>     {
>+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::pointer _ValPtr;
>+      typedef _Rb_tree_node_traits<_ValPtr> _Node_traits;
>+
>+      typedef typename _Node_traits::_Node_base        _Node_base;
>+      typedef typename _Node_traits::_Node_type        _Node_type;
>+
>       typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
>-    rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
>+    rebind<_Node_type>::other _Node_allocator;
>
>       typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
>
>     protected:
>-      typedef _Rb_tree_node_base*         _Base_ptr;
>-      typedef const _Rb_tree_node_base*     _Const_Base_ptr;
>-      typedef _Rb_tree_node<_Val>*         _Link_type;
>-      typedef const _Rb_tree_node<_Val>*    _Const_Link_type;
>+      typedef typename _Node_base::_Base_ptr         _Base_ptr;
>+      typedef typename _Node_type::_Node_ptr         _Node_ptr;

Accessing a member of _Node_type here requires _Node_type to be
complete, which requires _Tp to be complete. That's allowed by the
standard, because std::map and std::set don't support incomplete types
(unlike vector and the linked lists).

But we don't really ned to instantiate _Node_type here, we could use
_Alloc_traits::pointer instead.


[...]

>@@ -1826,10 +2427,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>                   || _M_impl._M_key_compare(_KeyOfValue()(__v),
>                             _S_key(__p)));
>
>-    _Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
>+    _Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));

I think this relies on implicit conversion from _Node_ptr to
_Base_ptr, which is not necessarily possible. It works for the
_Pointer_adapter type in <ext/pointer.h> because it has a
template<typename U> _Pointer_adapter(_Pointer_adapter<U>) constructor
that supports implicit conversions.

There seems to be a few other places with this problem. Please take a
look at the Pointer type in the new test for my list and forward_list
fancy ptr patches (that Pointer should probably be added to the
testsuite/util/testsuite_allocator.h header for reuse).

I solved these problems by adding a _M_base_ptr() function to the
node_base type, which means that instead of relying on an implicit
conversion from node_ptr to _Base_ptr you call node_ptr->_M_base_ptr()
to get a _Base_ptr.

>@@ -2255,7 +2821,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     }
>       else
>     // Equivalent keys.
>-    return _Res(__pos._M_node, 0);
>+    return _Res(__position._M_node, 0);
>     }
>
>   template<typename _Key, typename _Val, typename _KeyOfValue,
>@@ -2294,11 +2860,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>     _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
>     {
>-      iterator __pos = __position._M_const_cast();

This removes __pos ...

>       typedef pair<_Base_ptr, _Base_ptr> _Res;
>
>       // end()
>-      if (__pos._M_node == _M_end())
>+      if (__position._M_node == _M_end())
>     {
>       if (size() > 0
>           && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
>@@ -2306,18 +2871,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       else
>         return _M_get_insert_equal_pos(__k);
>     }
>-      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
>+      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
>     {
>       // First, try before...
>       iterator __before = __pos;

But it's still used here.

>-      if (__pos._M_node == _M_leftmost()) // begin()
>+      if (__position._M_node == _M_leftmost()) // begin()
>         return _Res(_M_leftmost(), _M_leftmost());
>       else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
>         {
>           if (_S_right(__before._M_node) == 0)
>         return _Res(0, __before._M_node);
>           else
>-        return _Res(__pos._M_node, __pos._M_node);
>+        return _Res(__position._M_node, __position._M_node);
>         }
>       else
>         return _M_get_insert_equal_pos(__k);
>@@ -2326,12 +2891,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     {
>       // ... then try after.
>       iterator __after = __pos;

And here.

I think they should both be __position._M_const_cast().


>diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
>new file mode 100644
>index 00000000000..758beedccfd
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
>@@ -0,0 +1,44 @@
>+// { dg-do run { target c++11 } }
>+
>+#include <map>
>+#include <memory>
>+#include <testsuite_hooks.h>
>+#include <testsuite_allocator.h>
>+
>+struct T { int i; };
>+
>+bool operator<(const T& l, const T& r)
>+{ return l.i < r.i; }
>+
>+struct H

This struct H occurs in several tests but seems to be unused.

>+{
>+  std::size_t
>+  operator()(const T& t) const noexcept
>+  { return t.i; }
>+};
>+
>+struct L : std::less<T>
>+{ };
>+
>+using __gnu_test::CustomPointerAlloc;
>+
>+template class std::map<T, int, L,
>+            CustomPointerAlloc<std::pair<const T, int>>>;
>+
>+void test01()
>+{
>+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
>+  typedef std::map<T, int, L, alloc_type> test_type;
>+  test_type v;
>+  v.insert({ T(), 0 });
>+  VERIFY( v.begin()->second == 0 );
>+  VERIFY( ++v.begin() == v.end() );
>+  v.insert({ T { 1 }, 1 });
>+  VERIFY( v.size() == 2 );
>+  VERIFY( v.find(T()) != v.end() );
>+}
>+
>+int main()
>+{
>+  test01();
>+}

I'd be interested on your opinion on my patches for fancy pointer
support in the linked lists. I think it's cleaner and requires fewer
changes to the existing specializations.

My changes can also be disabled by a macro (to avoid breaking any
users who are somehow managing to use allocators-with-fancy-pointers
with RB trees today, which could be useful unless that's completely
broken today), and to be enabled for non-fancy pointers (for test
coverage).
  
François Dumont Nov. 30, 2024, 1:52 p.m. UTC | #14
Hi

I've applied all your comments below and the ones you did on the PR 
directly.

When all new types totally seperated from the legacy types 
_Rb_tree_node_traits is indeed useless.

Regarding _Rb_tree_helpers I got rid of it but move the 
insert-and-rebalance and rebalance-for-erase function to _Node_traits<>.

PR updated:

https://forge.sourceware.org/gcc/gcc-TEST/pulls/27

libstdc++: Add fancy pointer support in map and set Support fancy 
allocator pointer type in std::_Rb_tree<>. In case of fancy pointer type 
the container is now storing the pointer to __rb_tree::_Node<_ValPtr> as 
a pointer to __rb_tree::_Node_base<_VoidPtr>. Many methods are adapted 
to take and return _Base_ptr in place of _Link_type which has been 
renamed into _Node_ptr. As all node are stored as _Base_ptr have all 
methods working with this type and remove _Const_Base_ptr and 
_Const_Node_ptr and all methods associated with it. 
libstdc++-v3/ChangeLog: * include/bits/stl_tree.h 
[_GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE]: New macro to control usage of the 
code required to support fancy allocator pointer type. 
(_Rb_tree_node_base::_Const_Base_ptr): Remove. 
(_Rb_tree_node_base::_S_minimum, _Rb_tree_node_base::_S_maximum): Remove 
overloads for _Const_Base_ptr. (_Rb_tree_node_base::_M_base_ptr()): New. 
(_Rb_tree_node_base::_M_node_ptr<_NodePtr>()): New. 
(_Rb_tree_node::_Link_type): Rename into... (_Rb_tree_node::_Node_ptr): 
...this. (__rb_tree::_Node_base<>): New. (__rb_tree::_Header<>): New. 
(__rb_tree::_Node<>): New. (_Rb_tree_increment(const 
_Rb_tree_node_base*)): Remove declaration. (_Rb_tree_decrement(const 
_Rb_tree_node_base*)): Remove declaration. 
(_Rb_tree_iterator<>::_Link_type): Rename into... 
(_Rb_tree_iterator<>::_Node_ptr): ...this. 
(_Rb_tree_const_iterator<>::_Link_type): Rename into... 
(_Rb_tree_const_iterator<>::_Node_ptr): ...this. 
(_Rb_tree_const_iterator<>::_M_const_cast): Remove. 
(_Rb_tree_const_iterator<>::_M_node): Change type into _Base_ptr. 
(__rb_tree::_Iterator<>): New. (__rb_tree::_Node_traits<>): New. 
(_Rb_tree<>::_Node_base, _Rb_tree::_Node_type): New. 
(_Rb_tree<>::_Link_type): Rename into... (_Rb_tree<>::_Node_ptr): 
...this. (_Rb_tree<>::_Const_Base_ptr, _Rb_tree<>::_Const_Node_ptr): 
Remove. (_Rb_tree<>): Adapt to generalize usage of _Base_ptr in place of 
former _Link_type now _Node_ptr. (_Rb_tree<>::_M_mbegin): Remove. 
(_Rb_tree<>::_S_left(_Const_Base_ptr)): Remove. 
(_Rb_tree<>::_S_right(_Const_Base_ptr)): Remove. 
(_Rb_tree<>::_S_maximum(_Const_Base_ptr)): Remove. 
(_Rb_tree<>::_S_minimum(_Const_Base_ptr)): Remove. * 
testsuite/23_containers/map/allocator/ext_ptr.cc: New test case. * 
testsuite/23_containers/multimap/allocator/ext_ptr.cc: New test case. * 
testsuite/23_containers/multiset/allocator/ext_ptr.cc: New test case. * 
testsuite/23_containers/set/allocator/ext_ptr.cc: New test case.

I've tested it in C++98/C++17/C++20 with default 
_GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE and with it 9001.

François

On 18/11/2024 17:39, Jonathan Wakely wrote:
> On 18/11/24 06:57 +0100, François Dumont wrote:
>> Hi
>>
>> Here is a new proposal with all the cleanup regarding _Const_Base_ptr
>> that makes support of allocator's fancy pointer type simpler.
>>
>> Also submitted as PR:
>>
>> https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
>>
>>    libstdc++: Add fancy pointer support in map and set
>>
>>     Support fancy allocator pointer type instd::_Rb_tree<>.
>>
>>     In case of fancy pointer type the container is now storing the
>> pointer to
>>     _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.
>>
>>     Many methods are adapted to take and return _Base_ptr in place of
>> _Link_type
>>     which has been renamed into _Node_ptr.
>>
>>     As all node are stored as _Base_ptr have all methods working with
>> this type
>>     and remove _Const_Base_ptr and all methods associated to it.
>>
>>     libstdc++-v3/ChangeLog:
>>
>>             * include/bits/stl_set.h (std::set<>::upper_bound<>(const
>> _Kt&) const): Fix
>>             decltype typo to use const_iterator.
>>             * include/bits/stl_tree.h
>>             (_Rb_tree_ptr_traits<>): New.
>>             (_Rb_tree_pnode_base<>): New.
>>             (_Rb_tree_node_base): Inherit from latter.
>>             (_Rb_tree_node_base::_Const_Base_ptr): Remove.
>>             (_Rb_tree_node_base::_S_minimum(_Const_Base_ptr)): Remove.
>>             (_Rb_tree_node_base::_S_maximum(_Const_Base_ptr)): Remove.
>>             (_Rb_tree_pheader): New.
>>             (_Rb_tree_header): Inherit from latter.
>>             (_Rb_tree_node_val): New.
>>             (_Rb_tree_node): Inherit from latter.
>>             (_Rb_tree_pnode): New.
>>             (_Rb_tree_iterator<>::_Link_type): Rename into...
>>             (_Rb_tree_iterator<>::_Node_ptr): ...this.
>>             (_Rb_tree_const_iterator<>::_Link_type): Rename into...
>>             (_Rb_tree_const_iterator<>::_Node_ptr): ...this.
>>             (_Rb_tree_const_iterator<>::_M_node): Change type into
>> _Base_ptr.
>>             (_Rb_tree_const_iterator<>::_M_const_cast): Remove.
>>             (_Rb_tree_helpers<>): New.
>>             (_Rb_tree_piterator): New.
>>             (_Rb_tree_const_piterator): New.
>>             (_Rb_tree_node_traits<>): New.
>>             (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
>>             (_Rb_tree<>::_Const_Base_ptr): Remove.
>>             (_Rb_tree): Adapt to generalize usage of _Base_ptr in
>> place of _Link_type.
>>             (_Rb_tree<>::_M_mbegin): Remove.
>>             (_Rb_tree<>::_S_left(_Const_Base_ptr)): Remove.
>>             (_Rb_tree<>::_S_right(_Const_Base_ptr)): Remove.
>>             (_Rb_tree<>::_S_maximum(_Const_Base_ptr)): Remove.
>>             (_Rb_tree<>::_S_minimum(_Const_Base_ptr)): Remove.
>>             * testsuite/23_containers/map/allocator/ext_ptr.cc: New
>> test case.
>>             * testsuite/23_containers/multimap/allocator/ext_ptr.cc:
>> New test case.
>>             * testsuite/23_containers/multiset/allocator/ext_ptr.cc:
>> New test case.
>>             * testsuite/23_containers/set/allocator/ext_ptr.cc: New
>> test case.
>>
>> Tested under Linux x64.
>>
>>
>> Note that I've also run the 23_containers tests on map, multimap,
>> multiset and set tweaking implementation
>> so that new types are being used when C++11 or later even if allocator
>> pointer type is a C pointer.
> Please take a look at the _GLIBCXX_USE_ALLOC_PTR_xxx macros in
> https://gcc.gnu.org/pipermail/gcc-patches/2024-November/669128.html
> andhttps://gcc.gnu.org/pipermail/gcc-patches/2024-November/669127.html
> Those macros allow the fancy pointer support to be disabled, in case
> it will break anything for current users. Maybe more importantly, it
> allows the new node types to be used always, even for T*. That means
> we can run the entire testsuite using the new nodes without tweaking
> the implementation.
>
> Please consider a _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE macro (or some
> other name).
>
>> Ok to commit ?
>>
>> François
>>
>> On 12/11/2024 15:56, Jonathan Wakely wrote:
>>> On Mon, 4 Nov 2024 at 21:34, François Dumont<frs.dumont@gmail.com> wrote:
>>>> On 04/11/2024 19:45, Jonathan Wakely wrote:
>>>>> On Mon, 4 Nov 2024 at 18:30, François Dumont<frs.dumont@gmail.com> wrote:
>>>>>> On 21/10/2024 06:56, François Dumont wrote:
>>>>>>
>>>>>>
>>>>>> On 17/10/2024 23:11, Jonathan Wakely wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely<jwakely.gcc@gmail.com> wrote:
>>>>>>> On Thu, 17 Oct 2024 at 20:52, François Dumont<frs.dumont@gmail.com> wrote:
>>>>>>>> Here is an updated version that compiles, I think, all your feedbacks. It's much cleaner indeed.
>>>>>>> Thanks, I'll take a look tomorrow.
>>>>>>>
>>>>>>>> It's also tested in C++98/17/23.
>>>>>>>>
>>>>>>>> I'm surprised that we do not need to consider potentialallocator::const_pointer.
>>>>>>> Do you mean consider the case whereAlloc::const_pointer is not the same type as rebinding 'pointer' to a const element type?
>>>>>> Yes, exactly.
>>>>>>
>>>>>>
>>>>>>> We don't need to consider that because we never get a 'const_pointer' from the allocator, and we never need to pass a 'const_pointer' to the allocator. The allocator's 'allocate' and 'deallocate' members both work with the 'pointer' type, so we only need to use that type when interacting with the allocator. For all the other uses, such as _Const_Node_ptr, what we need is a pointer-to-const that's compatible with the allocator's pointer type. It doesn't actually matter if it's the same type as allocator_traits<Alloc>::const_pointer, because we don't need
>>>>>> Sorry, I sent the email before finishing that thought!
>>>>>>
>>>>>> ... we don't need to pass a const_pointer to anything, we only need it for the container's own purposes.
>>>>>>
>>>>>> But thinking about it some more, do we even need a const-pointer for the container?  Currently the const_iterator stores a const-pointer, and some members like _M_root() and _M_leftmost() return a const-pointer. But they don't need to. The nodes are all pointed to by a non-const _Base_ptr, none of the storage managed by the container is const.
>>>>>>
>>>>>> Except _M_impl._M_header in a const context.
>>>>>>
>>>>>> Using const_cast would result in a similar UB that you fixed on _GLIBCXX_DEBUG containers, no ?
>>>>> We only use a pointer to _M_header for the past-the-end iterator,
>>>>> which is not dereferenceable. It's OK to do the const_cast, it's not
>>>>> OK to write something through that pointer/reference if the object is
>>>>> really const. But you can't write through a past-the-end pointer
>>>>> anyway.
>>>>>
>>>> One last question then to complete this work, is it fine to change
>>>> _Rb_tree_const_iterator::_M_node to _Base_ptr ? No abi issue in doing so ?
>>> I thought I'd sent a reply to this question, but I don't see it in the
>>> archives, sorry.
>>>
>>> So changing from _Const_Base_ptr to _Base_ptr? Yes, I think that's OK.
>>>
>> diff --git a/libstdc++-v3/include/bits/stl_set.h b/libstdc++-v3/include/bits/stl_set.h
>> index c0eb4dbf65f..3d17626f330 100644
>> --- a/libstdc++-v3/include/bits/stl_set.h
>> +++ b/libstdc++-v3/include/bits/stl_set.h
>> @@ -875,7 +875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>        template<typename _Kt>
>>      auto
>>      upper_bound(const _Kt& __x) const
>> -    -> decltype(iterator(_M_t._M_upper_bound_tr(__x)))
>> +    -> decltype(const_iterator(_M_t._M_upper_bound_tr(__x)))
>>      { return const_iterator(_M_t._M_upper_bound_tr(__x)); }
>> #endif
>>        ///@}
>> diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
>> index bc27e191e8b..c6cba99726d 100644
>> --- a/libstdc++-v3/include/bits/stl_tree.h
>> +++ b/libstdc++-v3/include/bits/stl_tree.h
>> @@ -96,44 +96,100 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>
>>    enum _Rb_tree_color { _S_red = false, _S_black = true };
>>
>> -  struct _Rb_tree_node_base
>> -  {
>> -    typedef _Rb_tree_node_base* _Base_ptr;
>> -    typedef const _Rb_tree_node_base* _Const_Base_ptr;
>> -
>> -    _Rb_tree_color    _M_color;
>> -    _Base_ptr        _M_parent;
>> -    _Base_ptr        _M_left;
>> -    _Base_ptr        _M_right;
>> -
>> -    static _Base_ptr
>> -    _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>> +#if __cplusplus >= 201103L
>> +  template<typename _Ptr>
>> +    struct _Rb_tree_ptr_traits
>>      {
>> -      while (__x->_M_left != 0) __x = __x->_M_left;
>> -      return __x;
>> -    }
>> +      template<typename _Tp>
>> +    struct __rebind
>> +    { using __type = __ptr_rebind<_Ptr, _Tp>; };
> This only seems to be used in one place, can you just use __ptr_rebind
> directly in that one place?
>
>> +
>> +      template<typename _BasePtr>
>> +    static _Ptr
>> +    _S_ptr_to(_BasePtr __this) noexcept
>> +    { return pointer_traits<_Ptr>::pointer_to(*__this); }
> This is also only needed in exactly one place. I avoided needing this
> function by adding _M_base_ptr() to the node base classes.
>
> The whole of this _Rb_tree_ptr_traits class template seems unnecessary
> to me. Just inline the code directly into the places it's needed.
> You're not optimizing anything by replacing
> pointer_traits<T*>::pointer_to with
> _Rb_tree_ptr_traits<T*>::_S_ptr_to, it's still a class template
> specialization and a function call.
>
>> +
>> +      template<typename _BasePtr, typename _UpPtr>
>> +    static _UpPtr
>> +    _S_up_ptr_to(_BasePtr __this) noexcept
> pointer_to is not guaranteed to be noexcept.
>
> This appears to be a downcast, i.e. a base-to-derived cast, having
> "up" in the name is a bit confusing.
>
>> +    {
>> +      using __ptr_traits = pointer_traits<_UpPtr>;
>> +      using __up_type = typename __ptr_traits::element_type;
>> +      auto __up_this = static_cast<__up_type*>(__this);
>> +      return __ptr_traits::pointer_to(*__up_this);
>> +    }
>> +    };
>> +#else
>> +  template<typename _Ptr>
>> +    struct _Rb_tree_ptr_traits;
>> +#endif
>>
>> -    static _Const_Base_ptr
>> -    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>> +  template<typename _Tp>
>> +    struct _Rb_tree_ptr_traits<_Tp*>
>>      {
>> -      while (__x->_M_left != 0) __x = __x->_M_left;
>> -      return __x;
>> -    }
>> +      template<typename>
>> +    struct __rebind
>> +    { typedef _Tp* __type; };
>> +
>> +      template<typename _BasePtr>
>> +    static _Tp*
>> +    _S_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
>> +    { return static_cast<_Tp*>(__this); }
>> +
>> +      template<typename _BasePtr, typename _UpPtr>
>> +    static _UpPtr
>> +    _S_up_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
>> +    { return static_cast<_UpPtr>(__this); }
>> +    };
>>
>> -    static _Base_ptr
>> -    _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>> +  template<typename _BasePtr>
>> +    struct _Rb_tree_pnode_base
>>      {
>> -      while (__x->_M_right != 0) __x = __x->_M_right;
>> -      return __x;
>> -    }
>> +      typedef typename _Rb_tree_ptr_traits<_BasePtr>::template
>> +    __rebind<_Rb_tree_pnode_base>::__type _Base_ptr;
> The names _BasePtr and _Base_ptr are very similar, and confusing.
> It looks like sometimes the template argument that is substituted for
> _BasePtr is sometimes a derived class (_Rb_tree_node_base) and
> sometimes is _ValPtr. It's never a "base" pointer. Please rename it.
>
>> -    static _Const_Base_ptr
>> -    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>> -    {
>> -      while (__x->_M_right != 0) __x = __x->_M_right;
>> -      return __x;
>> -    }
>> -  };
>> +      _Base_ptr
>> +      _M_self_ptr() _GLIBCXX_NOEXCEPT
> Why do we need a non-const overload? The const one will work for all
> cases, right?
>
> I added a comment to my list patches pointing out that it's not
> const-correct, but is safe as long as only used carefully.
>
>
>> +      { return _Rb_tree_ptr_traits<_Base_ptr>::_S_ptr_to(this); }
>> +
>> +      _Base_ptr
>> +      _M_self_ptr() const _GLIBCXX_NOEXCEPT
>> +      {
>> +    _Rb_tree_pnode_base* __self = const_cast<_Rb_tree_pnode_base*>(this);
>> +    return __self->_M_self_ptr();
>> +      }
>> +
>> +      template<typename _NodePtr>
>> +    _NodePtr
>> +    _M_node_ptr() _GLIBCXX_NOEXCEPT
>> +    {
>> +      return _Rb_tree_ptr_traits<_Base_ptr>::template
>> +        _S_up_ptr_to<_Rb_tree_pnode_base*, _NodePtr>(this);
>> +    }
>> +
>> +      _Rb_tree_color    _M_color;
>> +      _Base_ptr        _M_parent;
>> +      _Base_ptr        _M_left;
>> +      _Base_ptr        _M_right;
>> +
>> +      static _Base_ptr
>> +      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>> +      {
>> +    while (__x->_M_left) __x = __x->_M_left;
>> +    return __x;
>> +      }
>> +
>> +      static _Base_ptr
>> +      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>> +      {
>> +    while (__x->_M_right) __x = __x->_M_right;
>> +    return __x;
>> +      }
>> +    };
>> +
>> +  struct _Rb_tree_node_base
>> +    : _Rb_tree_pnode_base<_Rb_tree_node_base*>
>> +  { };
> This approaches means that the existing specializations of RB trees
> all get twice as many base classes. I don't see an ABI problem, and it
> should generate the same code for optimized builds, but increasing the
> inheritance depth and the number of class template instantiations
> gives the compiler more work to do, compiling slower, and generating
> additional debug info (and maybe RTTI?)
>
> Please take a look at how I did it for the linked lists, where
> specializations for non-fancy pointers, e.g. _List_node<T>, are mostly
> unchanged. They don't get any new base classes, just some new typedefs
> and new accessors like:
>
> _Base_ptr _M_base() { return this; }
> const _List_node* _M_base() const { return this; }
>
> The _List_node_traits class template is used bystd::list etc. to
> select whether to use _List_node<Alloc::value_type> or
> __list::_Node<Alloc::pointer>. All the fancy pointer support is in the
> new type hierarchy, __list::_Node_base, __list::_Node, and
> __list::_Iterator. The old types like _List_node don't really change.
>
> I found that using the __list namespace with new types reduced a lot
> of confusion I had between the very similar names _List_node and
> _List_pnode (or _List_ptr_node, or other names like that).
>
> Maybe we could call it _List::_Node instead of __list::_Node, so it's
> a bit closer to _List_node but still distinguishable (and would
> produce very slightly shorter mangled names). I didn't do that,
> because our convention is to name impl namespaces as __foo not _Foo.
>
> That pattern could naturally be extended to __tree::_Node (or
> _Tree::_Node) as well.
>
>>    // Helper type offering value initialization guarantee on the compare functor.
>>    template<typename _Key_compare>
>> @@ -163,81 +219,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>      };
>>
>>    // Helper type to manage default initialization of node count and header.
>> -  struct _Rb_tree_header
>> -  {
>> -    _Rb_tree_node_base    _M_header;
>> -    size_t        _M_node_count; // Keeps track of size of tree.
>> -
>> -    _Rb_tree_header() _GLIBCXX_NOEXCEPT
>> +  template<typename _NodeBase>
>> +    struct _Rb_tree_pheader
>>      {
>> -      _M_header._M_color = _S_red;
>> -      _M_reset();
>> -    }
>> +    private:
>> +      typedef typename _NodeBase::_Base_ptr        _Base_ptr;
>> +
>> +    public:
>> +      _NodeBase        _M_header;
>> +      size_t        _M_node_count; // Keeps track of size of tree.
>> +
>> +      _Rb_tree_pheader() _GLIBCXX_NOEXCEPT
>> +      {
>> +    _M_header._M_color = _S_red;
>> +    _M_reset();
>> +      }
>>
>> #if __cplusplus >= 201103L
>> -    _Rb_tree_header(_Rb_tree_header&& __x) noexcept
>> -    {
>> -      if (__x._M_header._M_parent != nullptr)
>> -    _M_move_data(__x);
>> -      else
>> -    {
>> -      _M_header._M_color = _S_red;
>> -      _M_reset();
>> -    }
>> -    }
>> +      _Rb_tree_pheader(_Rb_tree_pheader&& __x) noexcept
>> +      {
>> +    if (__x._M_header._M_parent)
>> +      _M_move_data(__x);
>> +    else
>> +      {
>> +        _M_header._M_color = _S_red;
>> +        _M_reset();
>> +      }
>> +      }
>> #endif
>>
>> -    void
>> -    _M_move_data(_Rb_tree_header& __from)
>> -    {
>> -      _M_header._M_color = __from._M_header._M_color;
>> -      _M_header._M_parent = __from._M_header._M_parent;
>> -      _M_header._M_left = __from._M_header._M_left;
>> -      _M_header._M_right = __from._M_header._M_right;
>> -      _M_header._M_parent->_M_parent = &_M_header;
>> -      _M_node_count = __from._M_node_count;
>> -
>> -      __from._M_reset();
>> -    }
>> +      void
>> +      _M_move_data(_Rb_tree_pheader& __from)
>> +      {
>> +    _M_header._M_color = __from._M_header._M_color;
>> +    _M_header._M_parent = __from._M_header._M_parent;
>> +    _M_header._M_left = __from._M_header._M_left;
>> +    _M_header._M_right = __from._M_header._M_right;
>> +    _M_header._M_parent->_M_parent = _M_header._M_self_ptr();
>> +    _M_node_count = __from._M_node_count;
>> +
>> +    __from._M_reset();
>> +      }
>>
>> -    void
>> -    _M_reset()
>> -    {
>> -      _M_header._M_parent = 0;
>> -      _M_header._M_left = &_M_header;
>> -      _M_header._M_right = &_M_header;
>> -      _M_node_count = 0;
>> -    }
>> -  };
>> +      void
>> +      _M_reset()
>> +      {
>> +    _M_header._M_parent = _Base_ptr();
>> +    _M_header._M_left = _M_header._M_right = _M_header._M_self_ptr();
>> +    _M_node_count = 0;
>> +      }
>> +    };
>> +
>> +  struct _Rb_tree_header : _Rb_tree_pheader<_Rb_tree_node_base>
>> +  { };
>>
>>    template<typename _Val>
>> -    struct _Rb_tree_node : public _Rb_tree_node_base
>> +    struct _Rb_tree_node_val
>>      {
>> -      typedef _Rb_tree_node<_Val>* _Link_type;
>> -
>> #if __cplusplus < 201103L
>>        _Val _M_value_field;
>>
>> +      __attribute__((__always_inline__))
>>        _Val*
>>        _M_valptr()
>>        { returnstd::__addressof(_M_value_field); }
>>
>> +      __attribute__((__always_inline__))
>>        const _Val*
>>        _M_valptr() const
>>        { returnstd::__addressof(_M_value_field); }
>> #else
>>        __gnu_cxx::__aligned_membuf<_Val> _M_storage;
> For the new __list::_Node type I took the opportunity to replace
> __aligned_membuf with a union { _Val _M_data; }; which is simpler and
> produces the same code. We could probably make that change for the
> existing node types, I think it's safe, but making it only for the new
> _Node type means the old node type isn't affected.
>
>
>> +      [[__gnu__::__always_inline__]]
>>        _Val*
>>        _M_valptr()
>>        { return _M_storage._M_ptr(); }
>>
>> +      [[__gnu__::__always_inline__]]
>>        const _Val*
>>        _M_valptr() const
>>        { return _M_storage._M_ptr(); }
>> #endif
>>      };
>>
>> +  template<typename _Val>
>> +    struct _Rb_tree_node
>> +    : _Rb_tree_node_base
>> +    , _Rb_tree_node_val<_Val>
>> +    {
>> +      typedef _Rb_tree_node<_Val>* _Node_ptr;
>> +    };
>> +
>> +#if __cplusplus >= 201103L
>> +  template<typename _ValPtr>
>> +    struct _Rb_tree_pnode
>> +    : _Rb_tree_pnode_base<_ValPtr>
> This makes _Rb_tree_pnode_base depend on the value_type, which means
> you instantiate a different specialization of _Rb_tree_pnode_base for
> every _Rb_tree. There's no point even having a common base class if
> every specialization is different. You might as well just put the
> _M_parent/_M_left/_M_right pointers in _Rb_tree_pnode directly.
>
> The point of the node_base class is to have a common base that is not
> dependent on the value type, so thatstd::set<int> andstd::set<long>
> andstd::set<int, Cmp> all use the same node base. The way this patch
> works,std::set<int> will use _Rb_tree_pnode_base<int*> and
> std::set<long> will use _Rb_tree_pnode_base<long*>.
>
> This is why my patch for lists uses __ptr_rebind<_ValPtr, void> as the
> template argument to _Node_base, so that they all use
> _Node_base<void*>.
>
> Obviously for a fancy pointer, it will be a different specialization,
> sostd::list<int, A<int>> uses _Node_base<A::void_pointer>, but
> std::list<long, A<long>> uses the same _Node_base<A::void_pointer>.
>
>> +    , _Rb_tree_node_val<typenamestd::pointer_traits<_ValPtr>::element_type>
>> +    {
>> +      using _Node_ptr = __ptr_rebind<_ValPtr, _Rb_tree_pnode>;
>> +    };
>> +#endif
>> +
>>    _GLIBCXX_PURE _Rb_tree_node_base*
>>    _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
>>
> [...]
>
>> +#if __cplusplus >= 201103L
>> +  template<typename _ValPtr>
>> +    struct _Rb_tree_piterator
>> +    {
>> +      using __ptr_traits = pointer_traits<_ValPtr>;
>> +      using value_type = typename __ptr_traits::element_type;
>> +      using reference = value_type&;
>> +      using pointer = value_type*;
>> +
>> +      typedef bidirectional_iterator_tag iterator_category;
>> +      typedef ptrdiff_t             difference_type;
>> +
>> +      typedef _Rb_tree_piterator            _Self;
>> +      typedef _Rb_tree_pnode_base<_ValPtr>        _Node_base;
>> +      typedef _Rb_tree_pnode<_ValPtr>            _Node_type;
>> +      typedef typename _Node_type::_Base_ptr        _Base_ptr;
>> +      typedef typename _Node_type::_Node_ptr        _Node_ptr;
>> +      typedef _Rb_tree_helpers<_Node_base>        _Tree_helpers;
>> +
>> +      _Rb_tree_piterator() _GLIBCXX_NOEXCEPT
>> +      : _M_node() { }
>> +
>> +      explicit
>> +      _Rb_tree_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>> +      : _M_node(__x) { }
>> +
>> +      reference
>> +      operator*() const _GLIBCXX_NOEXCEPT
>> +      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>> +
>> +      pointer
>> +      operator->() const _GLIBCXX_NOEXCEPT
>> +      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>> +
>> +      _Self&
>> +      operator++() _GLIBCXX_NOEXCEPT
>> +      {
>> +    _M_node = _Tree_helpers::_Increment(_M_node);
>> +    return *this;
>> +      }
>> +
>> +      _Self
>> +      operator++(int) _GLIBCXX_NOEXCEPT
>> +      {
>> +    _Self __tmp = *this;
>> +    _M_node = _Tree_helpers::_Increment(_M_node);
>> +    return __tmp;
>> +      }
>> +
>> +      _Self&
>> +      operator--() _GLIBCXX_NOEXCEPT
>> +      {
>> +    _M_node = _Tree_helpers::_Decrement(_M_node);
>> +    return *this;
>> +      }
>> +
>> +      _Self
>> +      operator--(int) _GLIBCXX_NOEXCEPT
>> +      {
>> +    _Self __tmp = *this;
>> +    _M_node = _Tree_helpers::_Decrement(_M_node);
>> +    return __tmp;
>> +      }
>> +
>> +      friend bool
>> +      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>> +      { return __x._M_node == __y._M_node; }
>> +
>> +#if ! __cpp_lib_three_way_comparison
>> +      friend bool
>> +      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>> +      { return __x._M_node != __y._M_node; }
>> +#endif
>> +
>> +      _Base_ptr _M_node;
>> +    };
>> +
>> +  template<typename _ValPtr>
>> +    struct _Rb_tree_const_piterator
> For the linked lists, I didn't add two new iterator types, just
> _Iterator<_Const, _ValPtr> which is used for iterator and
> const_iterator.
>
> It seemed nice not to duplicate the iterator type, since the iterator
> and const_iterator are 95% the same. If we don't need to support
> C++98, we can write a single iterator implementation.
>
> If you have a single iterator implementation, you probably don't need
> the _Rb_tree_helpers.
>
> There would still be uses of insert-and-rebalance and
> rebalance-for-erase, but those could just be new overloads for fancy
> pointers. I don't think we need the _Rb_tree_helpers class template.
>
>
>> +    {
>> +      using __ptr_traits = pointer_traits<_ValPtr>;
>> +      using value_type = typename __ptr_traits::element_type;
>> +      using reference = const value_type&;
>> +      using pointer = const value_type*;
>> +
>> +      typedef _Rb_tree_piterator<_ValPtr>        iterator;
>> +      typedef typenameiterator::_Base_ptr        _Base_ptr;
>> +
>> +      typedef bidirectional_iterator_tag iterator_category;
>> +      typedef ptrdiff_t             difference_type;
>> +
>> +      typedef _Rb_tree_const_piterator            _Self;
>> +      typedef _Rb_tree_pnode_base<_ValPtr>        _Node_base;
>> +      typedef _Rb_tree_pnode<_ValPtr>            _Node_type;
>> +      typedef _Rb_tree_helpers<_Node_base>        _Tree_helpers;
>> +
>> +      _Rb_tree_const_piterator() _GLIBCXX_NOEXCEPT
>> +      : _M_node() { }
>> +
>> +      explicit
>> +      _Rb_tree_const_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>> +      : _M_node(__x) { }
>> +
>> +      _Rb_tree_const_piterator(const iterator& __it) _GLIBCXX_NOEXCEPT
>> +      : _M_node(__it._M_node) { }
>> +
>> +      reference
>> +      operator*() const _GLIBCXX_NOEXCEPT
>> +      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>> +
>> +      pointer
>> +      operator->() const _GLIBCXX_NOEXCEPT
>> +      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>> +
>> +      _Self&
>> +      operator++() _GLIBCXX_NOEXCEPT
>> +      {
>> +    _M_node = _Tree_helpers::_Increment(_M_node);
> You need the iterator increment code here, but you can define it
> inline without the _Increment helper.
>
>> +    return *this;
>> +      }
>> +
>> +      _Self
>> +      operator++(int) _GLIBCXX_NOEXCEPT
>> +      {
>> +    _Self __tmp = *this;
>> +    _M_node = _Tree_helpers::_Increment(_M_node);
> This could just use ++*this. So if there's no separate const iterator
> implementation, there's no more use of _Increment.
>
>> +    return __tmp;
>> +      }
>> +
>> +      _Self&
>> +      operator--() _GLIBCXX_NOEXCEPT
>> +      {
>> +    _M_node = _Tree_helpers::_Decrement(_M_node);
>> +    return *this;
>> +      }
>> +
>> +      _Self
>> +      operator--(int) _GLIBCXX_NOEXCEPT
>> +      {
>> +    _Self __tmp = *this;
>> +    _M_node = _Tree_helpers::_Decrement(_M_node);
>> +    return __tmp;
>> +      }
>> +
>> +      friend bool
>> +      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>> +      { return __x._M_node == __y._M_node; }
>> +
>> +#if ! __cpp_lib_three_way_comparison
>> +      friend bool
>> +      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>> +      { return __x._M_node != __y._M_node; }
>> +#endif
>> +
>> +      _Base_ptr _M_node;
>> +    };
>> +#endif // C++11
>> +
>> +#if __cplusplus >= 201103L
>> +  template<typename _ValPtr>
>> +    struct _Rb_tree_node_traits
>> +    {
>> +      using _Node_base        = _Rb_tree_pnode_base<_ValPtr>;
>> +      using _Node_type        = _Rb_tree_pnode<_ValPtr>;
>> +      using _Header_t        = _Rb_tree_pheader<_Node_base>;
>> +      using _Iterator_t        = _Rb_tree_piterator<_ValPtr>;
>> +      using _Const_iterator_t    = _Rb_tree_const_piterator<_ValPtr>;
>> +    };
>> +#else
>> +  template<typename _ValPtr>
>> +    struct _Rb_tree_node_traits;
>> +#endif
>> +
>> +  template<typename _Val>
>> +    struct _Rb_tree_node_traits<_Val*>
>> +    {
>> +      typedef _Rb_tree_node_base        _Node_base;
>> +      typedef _Rb_tree_node<_Val>        _Node_type;
>> +      typedef _Rb_tree_header            _Header_t;
>> +      typedef _Rb_tree_iterator<_Val>        _Iterator_t;
>> +      typedef _Rb_tree_const_iterator<_Val>    _Const_iterator_t;
>> +    };
>>
>> #if __cplusplus > 201402L
>>    template<typename _Tree1, typename _Cmp2>
>> @@ -424,16 +1068,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>         typename _Compare, typename _Alloc = allocator<_Val> >
>>      class _Rb_tree
>>      {
>> +      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::pointer _ValPtr;
>> +      typedef _Rb_tree_node_traits<_ValPtr> _Node_traits;
>> +
>> +      typedef typename _Node_traits::_Node_base        _Node_base;
>> +      typedef typename _Node_traits::_Node_type        _Node_type;
>> +
>>        typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
>> -    rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
>> +    rebind<_Node_type>::other _Node_allocator;
>>
>>        typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
>>
>>      protected:
>> -      typedef _Rb_tree_node_base*         _Base_ptr;
>> -      typedef const _Rb_tree_node_base*     _Const_Base_ptr;
>> -      typedef _Rb_tree_node<_Val>*         _Link_type;
>> -      typedef const _Rb_tree_node<_Val>*    _Const_Link_type;
>> +      typedef typename _Node_base::_Base_ptr         _Base_ptr;
>> +      typedef typename _Node_type::_Node_ptr         _Node_ptr;
> Accessing a member of _Node_type here requires _Node_type to be
> complete, which requires _Tp to be complete. That's allowed by the
> standard, becausestd::map andstd::set don't support incomplete types
> (unlike vector and the linked lists).
>
> But we don't really ned to instantiate _Node_type here, we could use
> _Alloc_traits::pointer instead.
>
>
> [...]
>
>> @@ -1826,10 +2427,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>                    || _M_impl._M_key_compare(_KeyOfValue()(__v),
>>                              _S_key(__p)));
>>
>> -    _Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
>> +    _Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
> I think this relies on implicit conversion from _Node_ptr to
> _Base_ptr, which is not necessarily possible. It works for the
> _Pointer_adapter type in <ext/pointer.h> because it has a
> template<typename U> _Pointer_adapter(_Pointer_adapter<U>) constructor
> that supports implicit conversions.
>
> There seems to be a few other places with this problem. Please take a
> look at the Pointer type in the new test for my list and forward_list
> fancy ptr patches (that Pointer should probably be added to the
> testsuite/util/testsuite_allocator.h header for reuse).
>
> I solved these problems by adding a _M_base_ptr() function to the
> node_base type, which means that instead of relying on an implicit
> conversion from node_ptr to _Base_ptr you call node_ptr->_M_base_ptr()
> to get a _Base_ptr.
>
>> @@ -2255,7 +2821,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>      }
>>        else
>>      // Equivalent keys.
>> -    return _Res(__pos._M_node, 0);
>> +    return _Res(__position._M_node, 0);
>>      }
>>
>>    template<typename _Key, typename _Val, typename _KeyOfValue,
>> @@ -2294,11 +2860,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>      _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>>      _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
>>      {
>> -      iterator __pos = __position._M_const_cast();
> This removes __pos ...
>
>>        typedef pair<_Base_ptr, _Base_ptr> _Res;
>>
>>        // end()
>> -      if (__pos._M_node == _M_end())
>> +      if (__position._M_node == _M_end())
>>      {
>>        if (size() > 0
>>            && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
>> @@ -2306,18 +2871,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>        else
>>          return _M_get_insert_equal_pos(__k);
>>      }
>> -      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
>> +      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
>>      {
>>        // First, try before...
>>        iterator __before = __pos;
> But it's still used here.
>
>> -      if (__pos._M_node == _M_leftmost()) // begin()
>> +      if (__position._M_node == _M_leftmost()) // begin()
>>          return _Res(_M_leftmost(), _M_leftmost());
>>        else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
>>          {
>>            if (_S_right(__before._M_node) == 0)
>>          return _Res(0, __before._M_node);
>>            else
>> -        return _Res(__pos._M_node, __pos._M_node);
>> +        return _Res(__position._M_node, __position._M_node);
>>          }
>>        else
>>          return _M_get_insert_equal_pos(__k);
>> @@ -2326,12 +2891,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>      {
>>        // ... then try after.
>>        iterator __after = __pos;
> And here.
>
> I think they should both be __position._M_const_cast().
>
>
>> diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
>> new file mode 100644
>> index 00000000000..758beedccfd
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
>> @@ -0,0 +1,44 @@
>> +// { dg-do run { target c++11 } }
>> +
>> +#include <map>
>> +#include <memory>
>> +#include <testsuite_hooks.h>
>> +#include <testsuite_allocator.h>
>> +
>> +struct T { int i; };
>> +
>> +bool operator<(const T& l, const T& r)
>> +{ return l.i < r.i; }
>> +
>> +struct H
> This struct H occurs in several tests but seems to be unused.
>
>> +{
>> +std::size_t
>> +  operator()(const T& t) const noexcept
>> +  { return t.i; }
>> +};
>> +
>> +struct L :std::less<T>
>> +{ };
>> +
>> +using __gnu_test::CustomPointerAlloc;
>> +
>> +template classstd::map<T, int, L,
>> +            CustomPointerAlloc<std::pair<const T, int>>>;
>> +
>> +void test01()
>> +{
>> +  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
>> +  typedefstd::map<T, int, L, alloc_type> test_type;
>> +  test_type v;
>> +  v.insert({ T(), 0 });
>> +  VERIFY( v.begin()->second == 0 );
>> +  VERIFY( ++v.begin() == v.end() );
>> +  v.insert({ T { 1 }, 1 });
>> +  VERIFY( v.size() == 2 );
>> +  VERIFY( v.find(T()) != v.end() );
>> +}
>> +
>> +int main()
>> +{
>> +  test01();
>> +}
> I'd be interested on your opinion on my patches for fancy pointer
> support in the linked lists. I think it's cleaner and requires fewer
> changes to the existing specializations.
>
> My changes can also be disabled by a macro (to avoid breaking any
> users who are somehow managing to use allocators-with-fancy-pointers
> with RB trees today, which could be useful unless that's completely
> broken today), and to be enabled for non-fancy pointers (for test
> coverage).
>
diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
index bc27e191e8b..93b02ac5938 100644
--- a/libstdc++-v3/include/bits/stl_tree.h
+++ b/libstdc++-v3/include/bits/stl_tree.h
@@ -74,6 +74,13 @@
 # include <bits/node_handle.h>
 #endif
 
+#if __cplusplus < 201103L
+# undef _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+# define _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE 0
+#elif ! defined _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+# define _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE 1
+#endif
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -99,7 +106,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   struct _Rb_tree_node_base
   {
     typedef _Rb_tree_node_base* _Base_ptr;
-    typedef const _Rb_tree_node_base* _Const_Base_ptr;
 
     _Rb_tree_color	_M_color;
     _Base_ptr		_M_parent;
@@ -113,13 +119,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return __x;
     }
 
-    static _Const_Base_ptr
-    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-    {
-      while (__x->_M_left != 0) __x = __x->_M_left;
-      return __x;
-    }
-
     static _Base_ptr
     _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
     {
@@ -127,12 +126,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return __x;
     }
 
-    static _Const_Base_ptr
-    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-    {
-      while (__x->_M_right != 0) __x = __x->_M_right;
-      return __x;
-    }
+    // This is not const-correct, but it's only used in a const access path
+    // by std::_Rb_tree::_M_end() where the pointer is used to initialize a
+    // const_iterator and so constness is restored.
+    _Base_ptr
+    _M_base_ptr() const _GLIBCXX_NOEXCEPT
+    { return const_cast<_Rb_tree_node_base*>(this); }
+
+    template<typename _NodePtr>
+      _NodePtr
+      _M_node_ptr() _GLIBCXX_NOEXCEPT
+      { return static_cast<_NodePtr>(this); }
   };
 
   // Helper type offering value initialization guarantee on the compare functor.
@@ -213,7 +217,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Val>
     struct _Rb_tree_node : public _Rb_tree_node_base
     {
-      typedef _Rb_tree_node<_Val>* _Link_type;
+      typedef _Rb_tree_node<_Val>* _Node_ptr;
 
 #if __cplusplus < 201103L
       _Val _M_value_field;
@@ -238,18 +242,136 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
     };
 
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+namespace __rb_tree
+{
+  template<typename _VoidPtr>
+    struct _Node_base
+    {
+      using _Base_ptr = __ptr_rebind<_VoidPtr, _Node_base>;
+
+      _Rb_tree_color	_M_color;
+      _Base_ptr		_M_parent;
+      _Base_ptr		_M_left;
+      _Base_ptr		_M_right;
+
+      static _Base_ptr
+      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      {
+	while (__x->_M_left) __x = __x->_M_left;
+	return __x;
+      }
+
+      static _Base_ptr
+      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      {
+	while (__x->_M_right) __x = __x->_M_right;
+	return __x;
+      }
+
+      // This is not const-correct, but it's only used in a const access path
+      // by std::_Rb_tree::_M_end() where the pointer is used to initialize a
+      // const_iterator and so constness is restored.
+      _Base_ptr
+      _M_base_ptr() const
+      {
+	return pointer_traits<_Base_ptr>::pointer_to
+	  (*const_cast<_Node_base*>(this));
+      }
+
+      template<typename _NodePtr>
+	_NodePtr
+	_M_node_ptr() _GLIBCXX_NOEXCEPT
+	{
+	  using __ptr_traits = pointer_traits<_NodePtr>;
+	  using __node_type = typename __ptr_traits::element_type;
+	  auto __this = static_cast<__node_type*>(this);
+	  return __ptr_traits::pointer_to(*__this);
+	}
+    };
+
+  // Helper type to manage default initialization of node count and header.
+  template<typename _NodeBase>
+    struct _Header
+    {
+    private:
+      using _Base_ptr =  typename _NodeBase::_Base_ptr;
+
+    public:
+      _NodeBase		_M_header;
+      size_t		_M_node_count; // Keeps track of size of tree.
+
+      _Header() noexcept
+      {
+	_M_header._M_color = _S_red;
+	_M_reset();
+      }
+
+      _Header(_Header&& __x) noexcept
+      {
+	if (__x._M_header._M_parent)
+	  _M_move_data(__x);
+	else
+	  {
+	    _M_header._M_color = _S_red;
+	    _M_reset();
+	  }
+      }
+
+      void
+      _M_move_data(_Header& __from)
+      {
+	_M_header._M_color = __from._M_header._M_color;
+	_M_header._M_parent = __from._M_header._M_parent;
+	_M_header._M_left = __from._M_header._M_left;
+	_M_header._M_right = __from._M_header._M_right;
+	_M_header._M_parent->_M_parent = _M_header._M_base_ptr();
+	_M_node_count = __from._M_node_count;
+
+	__from._M_reset();
+      }
+
+      void
+      _M_reset()
+      {
+	_M_header._M_parent = _Base_ptr{};
+	_M_header._M_left = _M_header._M_right = _M_header._M_base_ptr();
+	_M_node_count = 0;
+      }
+    };
+
+  template<typename _ValPtr>
+    struct _Node : public __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>
+    {
+      using value_type = typename pointer_traits<_ValPtr>::element_type;
+      using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
+
+      union
+      {
+	value_type _M_storage;
+      };
+
+      _Node() { }
+      ~_Node() { }
+      _Node(_Node&&) = delete;
+
+      value_type*
+      _M_valptr()
+      { return std::__addressof(_M_storage); }
+
+      value_type const*
+      _M_valptr() const
+      { return std::__addressof(_M_storage); }
+    };
+} // namespace __rb_tree
+#endif
+
   _GLIBCXX_PURE _Rb_tree_node_base*
   _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
 
-  _GLIBCXX_PURE const _Rb_tree_node_base*
-  _Rb_tree_increment(const _Rb_tree_node_base* __x) throw ();
-
   _GLIBCXX_PURE _Rb_tree_node_base*
   _Rb_tree_decrement(_Rb_tree_node_base* __x) throw ();
 
-  _GLIBCXX_PURE const _Rb_tree_node_base*
-  _Rb_tree_decrement(const _Rb_tree_node_base* __x) throw ();
-
   template<typename _Tp>
     struct _Rb_tree_iterator
     {
@@ -262,7 +384,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       typedef _Rb_tree_iterator<_Tp>		_Self;
       typedef _Rb_tree_node_base::_Base_ptr	_Base_ptr;
-      typedef _Rb_tree_node<_Tp>*		_Link_type;
+      typedef _Rb_tree_node<_Tp>*		_Node_ptr;
 
       _Rb_tree_iterator() _GLIBCXX_NOEXCEPT
       : _M_node() { }
@@ -273,11 +395,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       reference
       operator*() const _GLIBCXX_NOEXCEPT
-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type> (_M_node)->_M_valptr(); }
+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       _Self&
       operator++() _GLIBCXX_NOEXCEPT
@@ -334,9 +456,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef bidirectional_iterator_tag iterator_category;
       typedef ptrdiff_t			 difference_type;
 
-      typedef _Rb_tree_const_iterator<_Tp>		_Self;
-      typedef _Rb_tree_node_base::_Const_Base_ptr	_Base_ptr;
-      typedef const _Rb_tree_node<_Tp>*			_Link_type;
+      typedef _Rb_tree_const_iterator<_Tp>	_Self;
+      typedef _Rb_tree_node_base::_Base_ptr	_Base_ptr;
+      typedef const _Rb_tree_node<_Tp>*		_Node_ptr;
 
       _Rb_tree_const_iterator() _GLIBCXX_NOEXCEPT
       : _M_node() { }
@@ -348,17 +470,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree_const_iterator(const iterator& __it) _GLIBCXX_NOEXCEPT
       : _M_node(__it._M_node) { }
 
-      iterator
-      _M_const_cast() const _GLIBCXX_NOEXCEPT
-      { return iterator(const_cast<typename iterator::_Base_ptr>(_M_node)); }
-
       reference
       operator*() const _GLIBCXX_NOEXCEPT
-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       _Self&
       operator++() _GLIBCXX_NOEXCEPT
@@ -415,6 +533,482 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
 			       _Rb_tree_node_base& __header) throw ();
 
+namespace __rb_tree
+{
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+  template<bool _Const, typename _ValPtr>
+    struct _Iterator
+    {
+      template<typename _Tp>
+	using __maybe_const = __conditional_t<_Const, const _Tp, _Tp>;
+
+      using __ptr_traits =	pointer_traits<_ValPtr>;
+      using value_type =	typename __ptr_traits::element_type;
+      using reference =		__maybe_const<value_type>&;
+      using pointer =		__maybe_const<value_type>*;
+
+      using iterator_category =	bidirectional_iterator_tag;
+      using difference_type =	ptrdiff_t;
+
+      using _Self =	 _Iterator;
+      using _Node_type = __rb_tree::_Node<_ValPtr>;
+      using _Node_base = __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>;
+      using _Base_ptr =	 typename _Node_base::_Base_ptr;
+
+      _Iterator() _GLIBCXX_NOEXCEPT
+      : _M_node() { }
+
+      explicit
+      _Iterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      : _M_node(__x) { }
+
+#if __cplusplus >= 201103L
+      _Iterator(const _Iterator&) = default;
+      _Iterator& operator=(const _Iterator&) = default;
+#endif
+
+#ifdef __glibcxx_concepts
+      constexpr
+      _Iterator(const _Iterator<false, _ValPtr>& __it) requires _Const
+#else
+      template<bool _OtherConst,
+	       typename = __enable_if_t<_Const && !_OtherConst>>
+	constexpr
+	_Iterator(const _Iterator<_OtherConst, _ValPtr>& __it)
+#endif
+	: _M_node(__it._M_node) { }
+
+      reference
+      operator*() const _GLIBCXX_NOEXCEPT
+      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
+
+      pointer
+      operator->() const _GLIBCXX_NOEXCEPT
+      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
+
+      _Self&
+      operator++() _GLIBCXX_NOEXCEPT
+      {
+	if (_M_node->_M_right)
+	  {
+	    _M_node = _M_node->_M_right;
+	    while (_M_node->_M_left)
+	      _M_node = _M_node->_M_left;
+	  }
+	else
+	  {
+	    _Base_ptr __y = _M_node->_M_parent;
+	    while (_M_node == __y->_M_right)
+	      {
+		_M_node = __y;
+		__y = __y->_M_parent;
+	      }
+	    if (_M_node->_M_right != __y)
+	      _M_node = __y;
+	  }
+
+	return *this;
+      }
+
+      _Self
+      operator++(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	++*this;
+	return __tmp;
+      }
+
+      _Self&
+      operator--() _GLIBCXX_NOEXCEPT
+      {
+	if (_M_node->_M_color == _S_red
+	    && _M_node->_M_parent->_M_parent == _M_node)
+	  _M_node = _M_node->_M_right;
+	else if (_M_node->_M_left)
+	  {
+	    _Base_ptr __y = _M_node->_M_left;
+	    while (__y->_M_right)
+	      __y = __y->_M_right;
+	    _M_node = __y;
+	  }
+	else
+	  {
+	    _Base_ptr __y = _M_node->_M_parent;
+	    while (_M_node == __y->_M_left)
+	      {
+		_M_node = __y;
+		__y = __y->_M_parent;
+	      }
+	    _M_node = __y;
+	  }
+	return *this;
+      }
+
+      _Self
+      operator--(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	--*this;
+	return __tmp;
+      }
+
+      friend bool
+      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node == __y._M_node; }
+
+#if ! __cpp_lib_three_way_comparison
+      friend bool
+      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node != __y._M_node; }
+#endif
+
+      _Base_ptr _M_node;
+    };
+#endif // USE_ALLOC_PTR_FOR_RB_TREE
+
+  // Determine the node and iterator types used by std::_Rb_tree.
+  template<typename _Val, typename _Ptr>
+    struct _Node_traits;
+
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE <= 9000
+  // Specialization for the simple case where the allocator's pointer type
+  // is the same type as value_type*.
+  // For ABI compatibility we can't change the types used for this case.
+  template<typename _Val>
+    struct _Node_traits<_Val, _Val*>
+    {
+      typedef _Rb_tree_node<_Val>		_Node_type;
+      typedef _Rb_tree_node_base		_Node_base;
+      typedef _Rb_tree_header			_Header_t;
+      typedef _Rb_tree_iterator<_Val>		_Iterator;
+      typedef _Rb_tree_const_iterator<_Val>	_Const_iterator;
+
+      __attribute__((__nonnull__))
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _Node_base* __x, _Node_base* __p,
+			      _Node_base& __header) _GLIBCXX_USE_NOEXCEPT
+      {
+	return _Rb_tree_insert_and_rebalance(__insert_left, __x, __p, __header);
+      }
+
+      __attribute__((__nonnull__,__returns_nonnull__))
+      static _Node_base*
+      _S_rebalance_for_erase(_Node_base* const __z,
+			     _Node_base& __header) _GLIBCXX_USE_NOEXCEPT
+      { return _Rb_tree_rebalance_for_erase(__z, __header); }
+    };
+#endif
+
+#if ! _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+  // Always use the T* specialization.
+  template<typename _Val, typename _Ptr>
+    struct _Node_traits
+    : _Node_traits<_Val, _Val*>
+    { };
+#else
+  // Primary template used when the allocator uses fancy pointers.
+  template<typename _Val, typename _ValPtr>
+    struct _Node_traits
+    {
+      using _Node_type = __rb_tree::_Node<_ValPtr>;
+      using _Node_base = __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>;
+      using _Header_t = __rb_tree::_Header<_Node_base>;
+      using _Iterator = __rb_tree::_Iterator<false, _ValPtr>;
+      using _Const_iterator = __rb_tree::_Iterator<true, _ValPtr>;
+
+      using _Base_ptr = typename _Node_base::_Base_ptr;
+
+      static void
+      _Rotate_left(_Base_ptr __x, _Base_ptr& __root)
+      {
+	const _Base_ptr __y = __x->_M_right;
+
+	__x->_M_right = __y->_M_left;
+	if (__y->_M_left)
+	  __y->_M_left->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_left)
+	  __x->_M_parent->_M_left = __y;
+	else
+	  __x->_M_parent->_M_right = __y;
+	__y->_M_left = __x;
+	__x->_M_parent = __y;
+      }
+
+      static void
+      _Rotate_right(_Base_ptr __x, _Base_ptr& __root)
+      {
+	const _Base_ptr __y = __x->_M_left;
+
+	__x->_M_left = __y->_M_right;
+	if (__y->_M_right)
+	  __y->_M_right->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_right)
+	  __x->_M_parent->_M_right = __y;
+	else
+	  __x->_M_parent->_M_left = __y;
+	__y->_M_right = __x;
+	__x->_M_parent = __y;
+      }
+
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _Base_ptr __x, _Base_ptr __p,
+			      _Node_base& __header)
+      {
+	_Base_ptr& __root = __header._M_parent;
+
+	// Initialize fields in new node to insert.
+	__x->_M_parent = __p;
+	__x->_M_left = __x->_M_right = nullptr;
+	__x->_M_color = _S_red;
+
+	// Insert.
+	// Make new node child of parent and maintain root, leftmost and
+	// rightmost nodes.
+	// N.B. First node is always inserted left.
+	if (__insert_left)
+	  {
+	    __p->_M_left = __x; // also makes leftmost = __x when __p == &__header
+
+	    if (std::__to_address(__p) == std::addressof(__header))
+	      {
+		__header._M_parent = __x;
+		__header._M_right = __x;
+	      }
+	    else if (__p == __header._M_left)
+	      __header._M_left = __x; // maintain leftmost pointing to min node
+	  }
+	else
+	  {
+	    __p->_M_right = __x;
+
+	    if (__p == __header._M_right)
+	      __header._M_right = __x; // maintain rightmost pointing to max node
+	  }
+	// Rebalance.
+	while (__x != __root
+	       && __x->_M_parent->_M_color == _S_red)
+	  {
+	    const _Base_ptr __xpp = __x->_M_parent->_M_parent;
+
+	    if (__x->_M_parent == __xpp->_M_left)
+	      {
+		const _Base_ptr __y = __xpp->_M_right;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_right)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_left(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_right(__xpp, __root);
+		  }
+	      }
+	    else
+	      {
+		const _Base_ptr __y = __xpp->_M_left;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_left)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_right(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_left(__xpp, __root);
+		  }
+	      }
+	  }
+	__root->_M_color = _S_black;
+      }
+
+      static _Base_ptr
+      _S_rebalance_for_erase(_Base_ptr __z, _Node_base& __header)
+      {
+	_Base_ptr& __root = __header._M_parent;
+	_Base_ptr& __leftmost = __header._M_left;
+	_Base_ptr& __rightmost = __header._M_right;
+	_Base_ptr __y = __z;
+	_Base_ptr __x{};
+	_Base_ptr __x_parent{};
+
+	if (!__y->_M_left)     // __z has at most one non-null child. y == z.
+	  __x = __y->_M_right;     // __x might be null.
+	else
+	  if (!__y->_M_right)  // __z has exactly one non-null child. y == z.
+	    __x = __y->_M_left;    // __x is not null.
+	  else
+	    {
+	      // __z has two non-null children.  Set __y to
+	      __y = __y->_M_right;   //   __z's successor.  __x might be null.
+	      while (__y->_M_left)
+		__y = __y->_M_left;
+	      __x = __y->_M_right;
+	    }
+	if (__y != __z)
+	  {
+	    // relink y in place of z.  y is z's successor
+	    __z->_M_left->_M_parent = __y;
+	    __y->_M_left = __z->_M_left;
+	    if (__y != __z->_M_right)
+	      {
+		__x_parent = __y->_M_parent;
+		if (__x)
+		  __x->_M_parent = __y->_M_parent;
+		__y->_M_parent->_M_left = __x;   // __y must be a child of _M_left
+		__y->_M_right = __z->_M_right;
+		__z->_M_right->_M_parent = __y;
+	      }
+	    else
+	      __x_parent = __y;
+	    if (__root == __z)
+	      __root = __y;
+	    else if (__z->_M_parent->_M_left == __z)
+	      __z->_M_parent->_M_left = __y;
+	    else
+	      __z->_M_parent->_M_right = __y;
+	    __y->_M_parent = __z->_M_parent;
+	    std::swap(__y->_M_color, __z->_M_color);
+	    __y = __z;
+	    // __y now points to node to be actually deleted
+	  }
+	else
+	  {                        // __y == __z
+	    __x_parent = __y->_M_parent;
+	    if (__x)
+	      __x->_M_parent = __y->_M_parent;
+	    if (__root == __z)
+	      __root = __x;
+	    else
+	      if (__z->_M_parent->_M_left == __z)
+		__z->_M_parent->_M_left = __x;
+	      else
+		__z->_M_parent->_M_right = __x;
+	    if (__leftmost == __z)
+	      {
+		if (!__z->_M_right)        // __z->_M_left must be null also
+		  __leftmost = __z->_M_parent;
+		// makes __leftmost == _M_header if __z == __root
+		else
+		  __leftmost = _Node_base::_S_minimum(__x);
+	      }
+	    if (__rightmost == __z)
+	      {
+		if (__z->_M_left == 0)         // __z->_M_right must be null also
+		  __rightmost = __z->_M_parent;
+		// makes __rightmost == _M_header if __z == __root
+		else                      // __x == __z->_M_left
+		  __rightmost = _Node_base::_S_maximum(__x);
+	      }
+	  }
+	if (__y->_M_color != _S_red)
+	  {
+	    while (__x != __root && (__x == 0 || __x->_M_color == _S_black))
+	      if (__x == __x_parent->_M_left)
+		{
+		  _Base_ptr __w = __x_parent->_M_right;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_left(__x_parent, __root);
+		      __w = __x_parent->_M_right;
+		    }
+		  if ((!__w->_M_left || __w->_M_left->_M_color == _S_black) &&
+		      (!__w->_M_right || __w->_M_right->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_right || __w->_M_right->_M_color == _S_black)
+			{
+			  __w->_M_left->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_right(__w, __root);
+			  __w = __x_parent->_M_right;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_right)
+			__w->_M_right->_M_color = _S_black;
+		      _Rotate_left(__x_parent, __root);
+		      break;
+		    }
+		}
+	      else
+		{
+		  // same as above, with _M_right <-> _M_left.
+		  _Base_ptr __w = __x_parent->_M_left;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_right(__x_parent, __root);
+		      __w = __x_parent->_M_left;
+		    }
+		  if ((!__w->_M_right || __w->_M_right->_M_color == _S_black) &&
+		      (!__w->_M_left || __w->_M_left->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_left || __w->_M_left->_M_color == _S_black)
+			{
+			  __w->_M_right->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_left(__w, __root);
+			  __w = __x_parent->_M_left;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_left)
+			__w->_M_left->_M_color = _S_black;
+		      _Rotate_right(__x_parent, __root);
+		      break;
+		    }
+		}
+	    if (__x)
+	      __x->_M_color = _S_black;
+	  }
+
+	return __y;
+      }
+    };
+#endif
+} // namespace __rb_tree
+
 #if __cplusplus > 201402L
   template<typename _Tree1, typename _Cmp2>
     struct _Rb_tree_merge_helper { };
@@ -425,15 +1019,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     class _Rb_tree
     {
       typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
-	rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
+	rebind<_Val>::other _Val_alloc_type;
+
+      typedef __gnu_cxx::__alloc_traits<_Val_alloc_type> _Val_alloc_traits;
+      typedef typename _Val_alloc_traits::pointer _ValPtr;
+      typedef __rb_tree::_Node_traits<_Val, _ValPtr> _Node_traits;
+
+      typedef typename _Node_traits::_Node_base		_Node_base;
+      typedef typename _Node_traits::_Node_type		_Node_type;
+
+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
+	rebind<_Node_type>::other _Node_allocator;
 
-      typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
+      typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Node_alloc_traits;
 
     protected:
-      typedef _Rb_tree_node_base* 		_Base_ptr;
-      typedef const _Rb_tree_node_base* 	_Const_Base_ptr;
-      typedef _Rb_tree_node<_Val>* 		_Link_type;
-      typedef const _Rb_tree_node<_Val>*	_Const_Link_type;
+      typedef typename _Node_base::_Base_ptr 		_Base_ptr;
+      typedef typename _Node_alloc_traits::pointer	_Node_ptr;
 
     private:
       // Functor recycling a pool of nodes and using allocation once the pool
@@ -445,13 +1047,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  if (_M_root)
 	    {
-	      _M_root->_M_parent = 0;
+	      _M_root->_M_parent = _Base_ptr();
 
 	      if (_M_nodes->_M_left)
 		_M_nodes = _M_nodes->_M_left;
 	    }
 	  else
-	    _M_nodes = 0;
+	    _M_nodes = _Base_ptr();
 	}
 
 #if __cplusplus >= 201103L
@@ -459,13 +1061,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
 	~_Reuse_or_alloc_node()
-	{ _M_t._M_erase(static_cast<_Link_type>(_M_root)); }
+	{ _M_t._M_erase(_M_root); }
 
 	template<typename _Arg>
-	  _Link_type
+	  _Base_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg)
 	  {
-	    _Link_type __node = static_cast<_Link_type>(_M_extract());
+	    _Base_ptr __node = _M_extract();
 	    if (__node)
 	      {
 		_M_t._M_destroy_node(__node);
@@ -489,7 +1091,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    {
 	      if (_M_nodes->_M_right == __node)
 		{
-		  _M_nodes->_M_right = 0;
+		  _M_nodes->_M_right = _Base_ptr();
 
 		  if (_M_nodes->_M_left)
 		    {
@@ -503,10 +1105,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		    }
 		}
 	      else // __node is on the left.
-		_M_nodes->_M_left = 0;
+		_M_nodes->_M_left = _Base_ptr();
 	    }
 	  else
-	    _M_root = 0;
+	    _M_root = _Base_ptr();
 
 	  return __node;
 	}
@@ -524,9 +1126,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: _M_t(__t) { }
 
 	template<typename _Arg>
-	  _Link_type
+	  _Base_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg) const
-	  { return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg)); }
+	  {
+	    return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg));
+	  }
 
       private:
 	_Rb_tree& _M_t;
@@ -556,18 +1160,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return allocator_type(_M_get_Node_allocator()); }
 
     protected:
-      _Link_type
+      _Node_ptr
       _M_get_node()
-      { return _Alloc_traits::allocate(_M_get_Node_allocator(), 1); }
+      { return _Node_alloc_traits::allocate(_M_get_Node_allocator(), 1); }
 
       void
-      _M_put_node(_Link_type __p) _GLIBCXX_NOEXCEPT
-      { _Alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1); }
+      _M_put_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
+      {
+	_Node_alloc_traits::deallocate
+	  (_M_get_Node_allocator(), __p->template _M_node_ptr<_Node_ptr>(), 1);
+      }
 
 #if __cplusplus < 201103L
       void
-      _M_construct_node(_Link_type __node, const value_type& __x)
+      _M_construct_node(_Base_ptr __p, const value_type& __x)
       {
+	_Node_ptr __node = static_cast<_Node_ptr>(__p);
 	__try
 	  { get_allocator().construct(__node->_M_valptr(), __x); }
 	__catch(...)
@@ -577,79 +1185,83 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  }
       }
 
-      _Link_type
+      _Base_ptr
       _M_create_node(const value_type& __x)
       {
-	_Link_type __tmp = _M_get_node();
+	_Base_ptr __tmp = _M_get_node()->_M_base_ptr();
 	_M_construct_node(__tmp, __x);
 	return __tmp;
       }
 #else
       template<typename... _Args>
 	void
-	_M_construct_node(_Link_type __node, _Args&&... __args)
+	_M_construct_node(_Base_ptr __p, _Args&&... __args)
 	{
+	  _Node_type& __node = static_cast<_Node_type&>(*__p);
 	  __try
 	    {
-	      ::new(__node) _Rb_tree_node<_Val>;
-	      _Alloc_traits::construct(_M_get_Node_allocator(),
-				       __node->_M_valptr(),
-				       std::forward<_Args>(__args)...);
+	      ::new(std::__addressof(__node)) _Node_type;
+	      _Node_alloc_traits::construct(_M_get_Node_allocator(),
+					    __node._M_valptr(),
+					    std::forward<_Args>(__args)...);
 	    }
 	  __catch(...)
 	    {
-	      __node->~_Rb_tree_node<_Val>();
-	      _M_put_node(__node);
+	      __node.~_Node_type();
+	      _M_put_node(__p);
 	      __throw_exception_again;
 	    }
 	}
 
       template<typename... _Args>
-	_Link_type
+	_Base_ptr
 	_M_create_node(_Args&&... __args)
 	{
-	  _Link_type __tmp = _M_get_node();
+	  _Base_ptr __tmp = _M_get_node()->_M_base_ptr();
 	  _M_construct_node(__tmp, std::forward<_Args>(__args)...);
 	  return __tmp;
 	}
 #endif
 
       void
-      _M_destroy_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_destroy_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
       {
 #if __cplusplus < 201103L
-	get_allocator().destroy(__p->_M_valptr());
+	get_allocator().destroy(static_cast<_Node_ptr>(__p)->_M_valptr());
 #else
-	_Alloc_traits::destroy(_M_get_Node_allocator(), __p->_M_valptr());
-	__p->~_Rb_tree_node<_Val>();
+	_Node_type& __node = static_cast<_Node_type&>(*__p);
+	_Node_alloc_traits::destroy(_M_get_Node_allocator(),
+				    __node._M_valptr());
+	__node.~_Node_type();
 #endif
       }
 
       void
-      _M_drop_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_drop_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
       {
 	_M_destroy_node(__p);
 	_M_put_node(__p);
       }
 
       template<bool _MoveValue, typename _NodeGen>
-	_Link_type
-	_M_clone_node(_Link_type __x, _NodeGen& __node_gen)
+	_Base_ptr
+	_M_clone_node(_Base_ptr __x, _NodeGen& __node_gen)
 	{
 #if __cplusplus >= 201103L
 	  using _Vp = __conditional_t<_MoveValue,
 				      value_type&&,
 				      const value_type&>;
 #endif
-	  _Link_type __tmp
-	    = __node_gen(_GLIBCXX_FORWARD(_Vp, *__x->_M_valptr()));
+	  _Base_ptr __tmp = __node_gen
+	    (_GLIBCXX_FORWARD(_Vp, *static_cast<_Node_type&>(*__x)._M_valptr()));
 	  __tmp->_M_color = __x->_M_color;
-	  __tmp->_M_left = 0;
-	  __tmp->_M_right = 0;
+	  __tmp->_M_left = __tmp->_M_right = _Base_ptr();
 	  return __tmp;
 	}
 
     protected:
+      typedef typename _Node_traits::_Header_t		_Header_t;
+
 #if _GLIBCXX_INLINE_VERSION
       template<typename _Key_compare>
 #else
@@ -660,7 +1272,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	struct _Rb_tree_impl
 	: public _Node_allocator
 	, public _Rb_tree_key_compare<_Key_compare>
-	, public _Rb_tree_header
+	, public _Header_t
 	{
 	  typedef _Rb_tree_key_compare<_Key_compare> _Base_key_compare;
 
@@ -672,9 +1284,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  { }
 
 	  _Rb_tree_impl(const _Rb_tree_impl& __x)
-	  : _Node_allocator(_Alloc_traits::_S_select_on_copy(__x))
+	  : _Node_allocator(_Node_alloc_traits::_S_select_on_copy(__x))
 	  , _Base_key_compare(__x._M_key_compare)
-	  , _Rb_tree_header()
+	  , _Header_t()
 	  { }
 
 #if __cplusplus < 201103L
@@ -694,7 +1306,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Rb_tree_impl(_Rb_tree_impl&& __x, _Node_allocator&& __a)
 	  : _Node_allocator(std::move(__a)),
 	    _Base_key_compare(std::move(__x)),
-	    _Rb_tree_header(std::move(__x))
+	    _Header_t(std::move(__x))
 	  { }
 
 	  _Rb_tree_impl(const _Key_compare& __comp, _Node_allocator&& __a)
@@ -710,7 +1322,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_root() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_parent; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_root() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_parent; }
 
@@ -718,7 +1330,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_leftmost() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_left; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_leftmost() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_left; }
 
@@ -726,35 +1338,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_rightmost() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_right; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_rightmost() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_right; }
 
-      _Link_type
-      _M_mbegin() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(this->_M_impl._M_header._M_parent); }
-
-      _Link_type
-      _M_begin() _GLIBCXX_NOEXCEPT
-      { return _M_mbegin(); }
-
-      _Const_Link_type
+      _Base_ptr
       _M_begin() const _GLIBCXX_NOEXCEPT
-      {
-	return static_cast<_Const_Link_type>
-	  (this->_M_impl._M_header._M_parent);
-      }
+      { return this->_M_impl._M_header._M_parent; }
 
       _Base_ptr
-      _M_end() _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
-
-      _Const_Base_ptr
       _M_end() const _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
+      { return this->_M_impl._M_header._M_base_ptr(); }
 
       static const _Key&
-      _S_key(_Const_Link_type __x)
+      _S_key(_Base_ptr __x)
       {
 #if __cplusplus >= 201103L
 	// If we're asking for the key we're presumably using the comparison
@@ -772,48 +1369,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 # endif // C++17
 #endif // C++11
 
-	return _KeyOfValue()(*__x->_M_valptr());
+	return _KeyOfValue()(*static_cast<const _Node_type&>(*__x)._M_valptr());
       }
 
-      static _Link_type
-      _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_left); }
-
-      static _Const_Link_type
-      _S_left(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_left); }
-
-      static _Link_type
-      _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_right); }
-
-      static _Const_Link_type
-      _S_right(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_right); }
-
-      static const _Key&
-      _S_key(_Const_Base_ptr __x)
-      { return _S_key(static_cast<_Const_Link_type>(__x)); }
-
       static _Base_ptr
-      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
-
-      static _Const_Base_ptr
-      _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
+      _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      { return __x->_M_left; }
 
       static _Base_ptr
-      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
-
-      static _Const_Base_ptr
-      _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
+      _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      { return __x->_M_right; }
 
     public:
-      typedef _Rb_tree_iterator<value_type>       iterator;
-      typedef _Rb_tree_const_iterator<value_type> const_iterator;
+      typedef typename _Node_traits::_Iterator		iterator;
+      typedef typename _Node_traits::_Const_iterator	const_iterator;
 
       typedef std::reverse_iterator<iterator>       reverse_iterator;
       typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
@@ -846,7 +1415,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_(_Base_ptr __x, _Base_ptr __y, _Arg&& __v, _NodeGen&);
 
       iterator
-      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Link_type __z);
+      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Base_ptr __z);
 
       template<typename _Arg>
 	iterator
@@ -857,10 +1426,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_equal_lower(_Arg&& __x);
 
       iterator
-      _M_insert_lower_node(_Base_ptr __p, _Link_type __z);
+      _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z);
 
       iterator
-      _M_insert_equal_lower_node(_Link_type __z);
+      _M_insert_equal_lower_node(_Base_ptr __z);
 #else
       template<typename _NodeGen>
 	iterator
@@ -879,22 +1448,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       enum { __as_lvalue, __as_rvalue };
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
-	_M_copy(_Link_type, _Base_ptr, _NodeGen&);
+	_Base_ptr
+	_M_copy(_Base_ptr, _Base_ptr, _NodeGen&);
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
+	_Base_ptr
 	_M_copy(const _Rb_tree& __x, _NodeGen& __gen)
 	{
-	  _Link_type __root =
-	    _M_copy<_MoveValues>(__x._M_mbegin(), _M_end(), __gen);
-	  _M_leftmost() = _S_minimum(__root);
-	  _M_rightmost() = _S_maximum(__root);
+	  _Base_ptr __root =
+	    _M_copy<_MoveValues>(__x._M_begin(), _M_end(), __gen);
+	  _M_leftmost() = _Node_base::_S_minimum(__root);
+	  _M_rightmost() = _Node_base::_S_maximum(__root);
 	  _M_impl._M_node_count = __x._M_impl._M_node_count;
 	  return __root;
 	}
 
-      _Link_type
+      _Base_ptr
       _M_copy(const _Rb_tree& __x)
       {
 	_Alloc_node __an(*this);
@@ -902,22 +1471,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       void
-      _M_erase(_Link_type __x);
-
-      iterator
-      _M_lower_bound(_Link_type __x, _Base_ptr __y,
-		     const _Key& __k);
+      _M_erase(_Base_ptr __x);
 
-      const_iterator
-      _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _Base_ptr
+      _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k) const;
 
-      iterator
-      _M_upper_bound(_Link_type __x, _Base_ptr __y,
-		     const _Key& __k);
-
-      const_iterator
-      _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _Base_ptr
+      _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k) const;
 
     public:
@@ -935,7 +1496,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x)
       : _M_impl(__x._M_impl)
       {
-	if (__x._M_root() != 0)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -947,7 +1508,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x, const allocator_type& __a)
       : _M_impl(__x._M_impl._M_key_compare, _Node_allocator(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -966,7 +1527,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a, false_type)
       : _M_impl(__x._M_impl._M_key_compare, std::move(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_move_data(__x, false_type{});
       }
 
@@ -974,9 +1535,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a)
       noexcept( noexcept(
 	_Rb_tree(std::declval<_Rb_tree&&>(), std::declval<_Node_allocator&&>(),
-		 std::declval<typename _Alloc_traits::is_always_equal>())) )
+		 std::declval<typename _Node_alloc_traits::is_always_equal>())) )
       : _Rb_tree(std::move(__x), std::move(__a),
-		 typename _Alloc_traits::is_always_equal{})
+		 typename _Node_alloc_traits::is_always_equal{})
       { }
 #endif
 
@@ -1001,11 +1562,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       end() _GLIBCXX_NOEXCEPT
-      { return iterator(&this->_M_impl._M_header); }
+      { return iterator(_M_end()); }
 
       const_iterator
       end() const _GLIBCXX_NOEXCEPT
-      { return const_iterator(&this->_M_impl._M_header); }
+      { return const_iterator(_M_end()); }
 
       reverse_iterator
       rbegin() _GLIBCXX_NOEXCEPT
@@ -1033,7 +1594,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       size_type
       max_size() const _GLIBCXX_NOEXCEPT
-      { return _Alloc_traits::max_size(_M_get_Node_allocator()); }
+      { return _Node_alloc_traits::max_size(_M_get_Node_allocator()); }
 
       void
       swap(_Rb_tree& __t)
@@ -1194,7 +1755,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	const_iterator __result = __position;
 	++__result;
 	_M_erase_aux(__position);
-	return __result._M_const_cast();
+	return iterator(__result._M_node);
       }
 
       // LWG 2059.
@@ -1235,7 +1796,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       erase(const_iterator __first, const_iterator __last)
       {
 	_M_erase_aux(__first, __last);
-	return __last._M_const_cast();
+	return iterator(__last._M_node);
       }
 #else
       void
@@ -1266,19 +1827,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       lower_bound(const key_type& __k)
-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
+      { return iterator(_M_lower_bound(_M_begin(), _M_end(), __k)); }
 
       const_iterator
       lower_bound(const key_type& __k) const
-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
+      {
+	return const_iterator
+	  (_M_lower_bound(_M_begin(), _M_end(), __k));
+      }
 
       iterator
       upper_bound(const key_type& __k)
-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
+      { return iterator(_M_upper_bound(_M_begin(), _M_end(), __k)); }
 
       const_iterator
       upper_bound(const key_type& __k) const
-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
+      {
+	return const_iterator
+	  (_M_upper_bound(_M_begin(), _M_end(), __k));
+      }
 
       pair<iterator, iterator>
       equal_range(const key_type& __k);
@@ -1293,7 +1860,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_find_tr(const _Kt& __k)
 	{
 	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_find_tr(__k)._M_const_cast();
+	  return iterator(__const_this->_M_find_tr(__k)._M_node);
 	}
 
       template<typename _Kt,
@@ -1301,7 +1868,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	const_iterator
 	_M_find_tr(const _Kt& __k) const
 	{
-	  auto __j = _M_lower_bound_tr(__k);
+	  const_iterator __j(_M_lower_bound_tr(__k));
 	  if (__j != end() && _M_impl._M_key_compare(__k, _S_key(__j._M_node)))
 	    __j = end();
 	  return __j;
@@ -1318,21 +1885,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename _Kt,
 	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	iterator
-	_M_lower_bound_tr(const _Kt& __k)
-	{
-	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_lower_bound_tr(__k)._M_const_cast();
-	}
-
-      template<typename _Kt,
-	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	const_iterator
+	_Base_ptr
 	_M_lower_bound_tr(const _Kt& __k) const
 	{
 	  auto __x = _M_begin();
 	  auto __y = _M_end();
-	  while (__x != 0)
+	  while (__x)
 	    if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	      {
 		__y = __x;
@@ -1340,26 +1898,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      }
 	    else
 	      __x = _S_right(__x);
-	  return const_iterator(__y);
-	}
-
-      template<typename _Kt,
-	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	iterator
-	_M_upper_bound_tr(const _Kt& __k)
-	{
-	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_upper_bound_tr(__k)._M_const_cast();
+	  return __y;
 	}
 
       template<typename _Kt,
 	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	const_iterator
+	_Base_ptr
 	_M_upper_bound_tr(const _Kt& __k) const
 	{
 	  auto __x = _M_begin();
 	  auto __y = _M_end();
-	  while (__x != 0)
+	  while (__x)
 	    if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	      {
 		__y = __x;
@@ -1367,7 +1916,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      }
 	    else
 	      __x = _S_right(__x);
-	  return const_iterator(__y);
+	  return __y;
 	}
 
       template<typename _Kt,
@@ -1377,7 +1926,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  const _Rb_tree* __const_this = this;
 	  auto __ret = __const_this->_M_equal_range_tr(__k);
-	  return { __ret.first._M_const_cast(), __ret.second._M_const_cast() };
+	  return
+	    { iterator(__ret.first._M_node), iterator(__ret.second._M_node) };
 	}
 
       template<typename _Kt,
@@ -1385,7 +1935,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	pair<const_iterator, const_iterator>
 	_M_equal_range_tr(const _Kt& __k) const
 	{
-	  auto __low = _M_lower_bound_tr(__k);
+	  const_iterator __low(_M_lower_bound_tr(__k));
 	  auto __high = __low;
 	  auto& __cmp = _M_impl._M_key_compare;
 	  while (__high != end() && !__cmp(__k, _S_key(__high._M_node)))
@@ -1401,7 +1951,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #if __cplusplus >= 201103L
       _Rb_tree&
       operator=(_Rb_tree&&)
-      noexcept(_Alloc_traits::_S_nothrow_move()
+      noexcept(_Node_alloc_traits::_S_nothrow_move()
 	       && is_nothrow_move_assignable<_Compare>::value);
 
       template<typename _Iterator>
@@ -1449,8 +1999,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    auto __res = _M_get_insert_unique_pos(__nh._M_key());
 	    if (__res.second)
 	      {
-		__ret.position
-		  = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+		__ret.position = _M_insert_node(__res.first, __res.second,
+						__nh._M_ptr->_M_base_ptr());
 		__nh.release();
 		__ret.inserted = true;
 	      }
@@ -1476,9 +2026,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
 	    auto __res = _M_get_insert_equal_pos(__nh._M_key());
 	    if (__res.second)
-	      __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+	      __ret = _M_insert_node(__res.first, __res.second,
+				     __nh._M_ptr->_M_base_ptr());
 	    else
-	      __ret = _M_insert_equal_lower_node(__nh._M_ptr);
+	      __ret = _M_insert_equal_lower_node(__nh._M_ptr->_M_base_ptr());
 	    __nh.release();
 	  }
 	return __ret;
@@ -1497,7 +2048,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    auto __res = _M_get_insert_hint_unique_pos(__hint, __nh._M_key());
 	    if (__res.second)
 	      {
-		__ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+		__ret = _M_insert_node(__res.first, __res.second,
+				       __nh._M_ptr->_M_base_ptr());
 		__nh.release();
 	      }
 	    else
@@ -1518,9 +2070,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
 	    auto __res = _M_get_insert_hint_equal_pos(__hint, __nh._M_key());
 	    if (__res.second)
-	      __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+	      __ret = _M_insert_node(__res.first, __res.second,
+				     __nh._M_ptr->_M_base_ptr());
 	    else
-	      __ret = _M_insert_equal_lower_node(__nh._M_ptr);
+	      __ret = _M_insert_equal_lower_node(__nh._M_ptr->_M_base_ptr());
 	    __nh.release();
 	  }
 	return __ret;
@@ -1530,10 +2083,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       node_type
       extract(const_iterator __pos)
       {
-	auto __ptr = _Rb_tree_rebalance_for_erase(
-	    __pos._M_const_cast()._M_node, _M_impl._M_header);
+	auto __ptr = _Node_traits::_S_rebalance_for_erase
+	  (__pos._M_node, _M_impl._M_header);
 	--_M_impl._M_node_count;
-	return { static_cast<_Link_type>(__ptr), _M_get_Node_allocator() };
+	return
+	  { __ptr->template _M_node_ptr<_Node_ptr>(), _M_get_Node_allocator() };
       }
 
       /// Extract a node.
@@ -1567,11 +2121,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr = _Node_traits::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
-		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+		  _M_insert_node(__res.first, __res.second, __ptr);
 		}
 	    }
 	}
@@ -1589,11 +2142,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr = _Node_traits::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
-		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+		  _M_insert_node(__res.first, __res.second, __ptr);
 		}
 	    }
 	}
@@ -1666,7 +2218,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
 	_Rb_tree& _M_t;
-	_Link_type _M_node;
+	_Base_ptr _M_node;
       };
 #endif // C++11
     };
@@ -1704,7 +2256,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_move_assign(_Rb_tree& __x, true_type)
     {
       clear();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	_M_move_data(__x, true_type());
       std::__alloc_on_move(_M_get_Node_allocator(),
 			   __x._M_get_Node_allocator());
@@ -1723,7 +2275,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // structure.
       _Reuse_or_alloc_node __roan(*this);
       _M_impl._M_reset();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	{
 	  _M_root() = _M_copy<__as_rvalue>(__x, __roan);
 	  __x.clear();
@@ -1735,11 +2287,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     operator=(_Rb_tree&& __x)
-    noexcept(_Alloc_traits::_S_nothrow_move()
+    noexcept(_Node_alloc_traits::_S_nothrow_move()
 	     && is_nothrow_move_assignable<_Compare>::value)
     {
       _M_impl._M_key_compare = std::move(__x._M_impl._M_key_compare);
-      _M_move_assign(__x, __bool_constant<_Alloc_traits::_S_nothrow_move()>());
+      _M_move_assign(__x,
+		     __bool_constant<_Node_alloc_traits::_S_nothrow_move()>());
       return *this;
     }
 
@@ -1780,11 +2333,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  // Note that _Key may be a constant type.
 #if __cplusplus >= 201103L
-	  if (_Alloc_traits::_S_propagate_on_copy_assign())
+	  if (_Node_alloc_traits::_S_propagate_on_copy_assign())
 	    {
 	      auto& __this_alloc = this->_M_get_Node_allocator();
 	      auto& __that_alloc = __x._M_get_Node_allocator();
-	      if (!_Alloc_traits::_S_always_equal()
+	      if (!_Node_alloc_traits::_S_always_equal()
 		  && __this_alloc != __that_alloc)
 		{
 		  // Replacement allocator cannot free existing storage, we need
@@ -1798,7 +2351,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Reuse_or_alloc_node __roan(*this);
 	  _M_impl._M_reset();
 	  _M_impl._M_key_compare = __x._M_impl._M_key_compare;
-	  if (__x._M_root() != 0)
+	  if (__x._M_root())
 	    _M_root() = _M_copy<__as_lvalue>(__x, __roan);
 	}
 
@@ -1822,14 +2375,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 		 _NodeGen& __node_gen)
       {
-	bool __insert_left = (__x != 0 || __p == _M_end()
+	bool __insert_left = (__x || __p == _M_end()
 			      || _M_impl._M_key_compare(_KeyOfValue()(__v),
 							_S_key(__p)));
 
-	_Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
+	_Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
 
-	_Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				      this->_M_impl._M_header);
+	_Node_traits::_S_insert_and_rebalance
+	  (__insert_left, __z, __p, this->_M_impl._M_header);
 	++_M_impl._M_node_count;
 	return iterator(__z);
       }
@@ -1851,10 +2404,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _KeyOfValue()(__v)));
 
-      _Link_type __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
-
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Base_ptr __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
+      _Node_traits::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -1872,9 +2424,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_insert_equal_lower(const _Val& __v)
 #endif
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = !_M_impl._M_key_compare(_S_key(__x), _KeyOfValue()(__v)) ?
@@ -1886,12 +2438,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Key, typename _Val, typename _KoV,
 	   typename _Compare, typename _Alloc>
     template<bool _MoveValues, typename _NodeGen>
-      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Link_type
+      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Base_ptr
       _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::
-      _M_copy(_Link_type __x, _Base_ptr __p, _NodeGen& __node_gen)
+      _M_copy(_Base_ptr __x, _Base_ptr __p, _NodeGen& __node_gen)
       {
 	// Structural copy. __x and __p must be non-null.
-	_Link_type __top = _M_clone_node<_MoveValues>(__x, __node_gen);
+	_Base_ptr __top = _M_clone_node<_MoveValues>(__x, __node_gen);
 	__top->_M_parent = __p;
 
 	__try
@@ -1902,9 +2454,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __p = __top;
 	    __x = _S_left(__x);
 
-	    while (__x != 0)
+	    while (__x)
 	      {
-		_Link_type __y = _M_clone_node<_MoveValues>(__x, __node_gen);
+		_Base_ptr __y = _M_clone_node<_MoveValues>(__x, __node_gen);
 		__p->_M_left = __y;
 		__y->_M_parent = __p;
 		if (__x->_M_right)
@@ -1926,13 +2478,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     void
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_erase(_Link_type __x)
+    _M_erase(_Base_ptr __x)
     {
       // Erase without rebalancing.
-      while (__x != 0)
+      while (__x)
 	{
 	  _M_erase(_S_right(__x));
-	  _Link_type __y = _S_left(__x);
+	  _Base_ptr __y = _S_left(__x);
 	  _M_drop_node(__x);
 	  __x = __y;
 	}
@@ -1941,65 +2493,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::iterator
-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Link_type __x, _Base_ptr __y,
-		   const _Key& __k)
-    {
-      while (__x != 0)
-	if (!_M_impl._M_key_compare(_S_key(__x), __k))
-	  __y = __x, __x = _S_left(__x);
-	else
-	  __x = _S_right(__x);
-      return iterator(__y);
-    }
-
-  template<typename _Key, typename _Val, typename _KeyOfValue,
-	   typename _Compare, typename _Alloc>
-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::const_iterator
+		      _Compare, _Alloc>::_Base_ptr
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	  __y = __x, __x = _S_left(__x);
 	else
 	  __x = _S_right(__x);
-      return const_iterator(__y);
+      return __y;
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::iterator
+		      _Compare, _Alloc>::_Base_ptr
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Link_type __x, _Base_ptr __y,
-		   const _Key& __k)
-    {
-      while (__x != 0)
-	if (_M_impl._M_key_compare(__k, _S_key(__x)))
-	  __y = __x, __x = _S_left(__x);
-	else
-	  __x = _S_right(__x);
-      return iterator(__y);
-    }
-
-  template<typename _Key, typename _Val, typename _KeyOfValue,
-	   typename _Compare, typename _Alloc>
-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::const_iterator
-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	  __y = __x, __x = _S_left(__x);
 	else
 	  __x = _S_right(__x);
-      return const_iterator(__y);
+      return __y;
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2011,9 +2531,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k)
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
 	    __x = _S_right(__x);
@@ -2021,13 +2541,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Link_type __xu(__x);
+	      _Base_ptr __xu(__x);
 	      _Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
-	      return pair<iterator,
-			  iterator>(_M_lower_bound(__x, __y, __k),
-				    _M_upper_bound(__xu, __yu, __k));
+	      return make_pair(iterator(_M_lower_bound(__x, __y, __k)),
+			       iterator(_M_upper_bound(__xu, __yu, __k)));
 	    }
 	}
       return pair<iterator, iterator>(iterator(__y),
@@ -2043,9 +2562,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k) const
     {
-      _Const_Link_type __x = _M_begin();
-      _Const_Base_ptr __y = _M_end();
-      while (__x != 0)
+      _Base_ptr __x = _M_begin();
+      _Base_ptr __y = _M_end();
+      while (__x)
 	{
 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
 	    __x = _S_right(__x);
@@ -2053,13 +2572,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Const_Link_type __xu(__x);
-	      _Const_Base_ptr __yu(__y);
+	      _Base_ptr __xu(__x);
+	      _Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
-	      return pair<const_iterator,
-			  const_iterator>(_M_lower_bound(__x, __y, __k),
-					  _M_upper_bound(__xu, __yu, __k));
+	      return make_pair(const_iterator(_M_lower_bound(__x, __y, __k)),
+			       const_iterator(_M_upper_bound(__xu, __yu, __k)));
 	    }
 	}
       return pair<const_iterator, const_iterator>(const_iterator(__y),
@@ -2073,12 +2591,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     swap(_Rb_tree& __t)
     _GLIBCXX_NOEXCEPT_IF(__is_nothrow_swappable<_Compare>::value)
     {
-      if (_M_root() == 0)
+      if (!_M_root())
 	{
-	  if (__t._M_root() != 0)
+	  if (__t._M_root())
 	    _M_impl._M_move_data(__t._M_impl);
 	}
-      else if (__t._M_root() == 0)
+      else if (!__t._M_root())
 	__t._M_impl._M_move_data(_M_impl);
       else
 	{
@@ -2093,8 +2611,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // No need to swap header's color as it does not change.
       std::swap(this->_M_impl._M_key_compare, __t._M_impl._M_key_compare);
 
-      _Alloc_traits::_S_on_swap(_M_get_Node_allocator(),
-				__t._M_get_Node_allocator());
+      _Node_alloc_traits::_S_on_swap(_M_get_Node_allocator(),
+				     __t._M_get_Node_allocator());
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2107,10 +2625,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_unique_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       bool __comp = true;
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __comp = _M_impl._M_key_compare(__k, _S_key(__x));
@@ -2126,7 +2644,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
       if (_M_impl._M_key_compare(_S_key(__j._M_node), __k))
 	return _Res(__x, __y);
-      return _Res(__j._M_node, 0);
+      return _Res(__j._M_node, _Base_ptr());
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2139,9 +2657,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_equal_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = _M_impl._M_key_compare(__k, _S_key(__x)) ?
@@ -2209,44 +2727,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_hint_unique_pos(const_iterator __position,
 				  const key_type& __k)
     {
-      iterator __pos = __position._M_const_cast();
       typedef pair<_Base_ptr, _Base_ptr> _Res;
 
       // end()
-      if (__pos._M_node == _M_end())
+      if (__position._M_node == _M_end())
 	{
 	  if (size() > 0
 	      && _M_impl._M_key_compare(_S_key(_M_rightmost()), __k))
-	    return _Res(0, _M_rightmost());
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else
 	    return _M_get_insert_unique_pos(__k);
 	}
-      else if (_M_impl._M_key_compare(__k, _S_key(__pos._M_node)))
+      else if (_M_impl._M_key_compare(__k, _S_key(__position._M_node)))
 	{
 	  // First, try before...
-	  iterator __before = __pos;
-	  if (__pos._M_node == _M_leftmost()) // begin()
+	  iterator __before(__position._M_node);
+	  if (__position._M_node == _M_leftmost()) // begin()
 	    return _Res(_M_leftmost(), _M_leftmost());
 	  else if (_M_impl._M_key_compare(_S_key((--__before)._M_node), __k))
 	    {
-	      if (_S_right(__before._M_node) == 0)
-		return _Res(0, __before._M_node);
+	      if (!_S_right(__before._M_node))
+		return _Res(_Base_ptr(), __before._M_node);
 	      else
-		return _Res(__pos._M_node, __pos._M_node);
+		return _Res(__position._M_node, __position._M_node);
 	    }
 	  else
 	    return _M_get_insert_unique_pos(__k);
 	}
-      else if (_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
+      else if (_M_impl._M_key_compare(_S_key(__position._M_node), __k))
 	{
 	  // ... then try after.
-	  iterator __after = __pos;
-	  if (__pos._M_node == _M_rightmost())
-	    return _Res(0, _M_rightmost());
+	  iterator __after(__position._M_node);
+	  if (__position._M_node == _M_rightmost())
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else if (_M_impl._M_key_compare(__k, _S_key((++__after)._M_node)))
 	    {
-	      if (_S_right(__pos._M_node) == 0)
-		return _Res(0, __pos._M_node);
+	      if (!_S_right(__position._M_node))
+		return _Res(_Base_ptr(), __position._M_node);
 	      else
 		return _Res(__after._M_node, __after._M_node);
 	    }
@@ -2255,7 +2772,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
       else
 	// Equivalent keys.
-	return _Res(__pos._M_node, 0);
+	return _Res(__position._M_node, _Base_ptr());
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2294,30 +2811,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
     {
-      iterator __pos = __position._M_const_cast();
       typedef pair<_Base_ptr, _Base_ptr> _Res;
 
       // end()
-      if (__pos._M_node == _M_end())
+      if (__position._M_node == _M_end())
 	{
 	  if (size() > 0
 	      && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
-	    return _Res(0, _M_rightmost());
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else
 	    return _M_get_insert_equal_pos(__k);
 	}
-      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
+      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
 	{
 	  // First, try before...
-	  iterator __before = __pos;
-	  if (__pos._M_node == _M_leftmost()) // begin()
+	  iterator __before(__position._M_node);
+	  if (__position._M_node == _M_leftmost()) // begin()
 	    return _Res(_M_leftmost(), _M_leftmost());
 	  else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
 	    {
-	      if (_S_right(__before._M_node) == 0)
-		return _Res(0, __before._M_node);
+	      if (!_S_right(__before._M_node))
+		return _Res(_Base_ptr(), __before._M_node);
 	      else
-		return _Res(__pos._M_node, __pos._M_node);
+		return _Res(__position._M_node, __position._M_node);
 	    }
 	  else
 	    return _M_get_insert_equal_pos(__k);
@@ -2325,18 +2841,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       else
 	{
 	  // ... then try after.
-	  iterator __after = __pos;
-	  if (__pos._M_node == _M_rightmost())
-	    return _Res(0, _M_rightmost());
+	  iterator __after(__position._M_node);
+	  if (__position._M_node == _M_rightmost())
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else if (!_M_impl._M_key_compare(_S_key((++__after)._M_node), __k))
 	    {
-	      if (_S_right(__pos._M_node) == 0)
-		return _Res(0, __pos._M_node);
+	      if (!_S_right(__position._M_node))
+		return _Res(_Base_ptr(), __position._M_node);
 	      else
 		return _Res(__after._M_node, __after._M_node);
 	    }
 	  else
-	    return _Res(0, 0);
+	    return _Res(_Base_ptr(), _Base_ptr());
 	}
     }
 
@@ -2373,15 +2889,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Link_type __z)
+    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Base_ptr __z)
     -> iterator
     {
-      bool __insert_left = (__x != 0 || __p == _M_end()
+      bool __insert_left = (__x || __p == _M_end()
 			    || _M_impl._M_key_compare(_S_key(__z),
 						      _S_key(__p)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Node_traits::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -2390,15 +2906,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_lower_node(_Base_ptr __p, _Link_type __z)
+    _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z)
     -> iterator
     {
       bool __insert_left = (__p == _M_end()
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _S_key(__z)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Node_traits::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -2407,12 +2923,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_equal_lower_node(_Link_type __z)
+    _M_insert_equal_lower_node(_Base_ptr __z)
     -> iterator
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = !_M_impl._M_key_compare(_S_key(__x), _S_key(__z)) ?
@@ -2487,10 +3003,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     _M_erase_aux(const_iterator __position)
     {
-      _Link_type __y =
-	static_cast<_Link_type>(_Rb_tree_rebalance_for_erase
-				(const_cast<_Base_ptr>(__position._M_node),
-				 this->_M_impl._M_header));
+      _Base_ptr __y = _Node_traits::_S_rebalance_for_erase
+	(__position._M_node, this->_M_impl._M_header);
       _M_drop_node(__y);
       --_M_impl._M_node_count;
     }
@@ -2527,7 +3041,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     find(const _Key& __k)
     {
-      iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
+      iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
       return (__j == end()
 	      || _M_impl._M_key_compare(__k,
 					_S_key(__j._M_node))) ? end() : __j;
@@ -2540,7 +3054,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     find(const _Key& __k) const
     {
-      const_iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
+      const_iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
       return (__j == end()
 	      || _M_impl._M_key_compare(__k,
 					_S_key(__j._M_node))) ? end() : __j;
@@ -2574,9 +3088,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       unsigned int __len = _Rb_tree_black_count(_M_leftmost(), _M_root());
       for (const_iterator __it = begin(); __it != end(); ++__it)
 	{
-	  _Const_Link_type __x = static_cast<_Const_Link_type>(__it._M_node);
-	  _Const_Link_type __L = _S_left(__x);
-	  _Const_Link_type __R = _S_right(__x);
+	  _Base_ptr __x = __it._M_node;
+	  _Base_ptr __L = _S_left(__x);
+	  _Base_ptr __R = _S_right(__x);
 
 	  if (__x->_M_color == _S_red)
 	    if ((__L && __L->_M_color == _S_red)
@@ -2592,9 +3106,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    return false;
 	}
 
-      if (_M_leftmost() != _Rb_tree_node_base::_S_minimum(_M_root()))
+      if (_M_leftmost() != _Node_base::_S_minimum(_M_root()))
 	return false;
-      if (_M_rightmost() != _Rb_tree_node_base::_S_maximum(_M_root()))
+      if (_M_rightmost() != _Node_base::_S_maximum(_M_root()))
 	return false;
       return true;
     }
diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..3d83738f86b
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
@@ -0,0 +1,37 @@
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::map<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::map<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( v.begin()->second == 0 );
+  VERIFY( ++v.begin() == v.end() );
+  v.insert({ T { 1 }, 1 });
+  VERIFY( v.size() == 2 );
+  VERIFY( v.find(T()) != v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..41689ef43b7
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
@@ -0,0 +1,33 @@
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multimap<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::multimap<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..589e6b0a617
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
@@ -0,0 +1,32 @@
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multiset<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::multiset<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..bbcf32b20be
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
@@ -0,0 +1,32 @@
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::set<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::set<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
  
Jonathan Wakely Nov. 30, 2024, 5:21 p.m. UTC | #15
On Sat, 30 Nov 2024, 13:52 François Dumont, <frs.dumont@gmail.com> wrote:

> Hi
>
> I've applied all your comments below and the ones you did on the PR
> directly.
>
> When all new types totally seperated from the legacy types
> _Rb_tree_node_traits is indeed useless.
>
> Regarding _Rb_tree_helpers I got rid of it but move the insert-and-rebalance
> and rebalance-for-erase function to _Node_traits<>.
>
> PR updated:
>
> https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
>
> libstdc++: Add fancy pointer support in map and set Support fancy
> allocator pointer type in std::_Rb_tree<>. In case of fancy pointer type
> the container is now storing the pointer to __rb_tree::_Node<_ValPtr> as
> a pointer to __rb_tree::_Node_base<_VoidPtr>. Many methods are adapted to
> take and return _Base_ptr in place of _Link_type which has been renamed
> into _Node_ptr. As all node are stored as _Base_ptr have all methods
> working with this type and remove _Const_Base_ptr and _Const_Node_ptr and
> all methods associated with it.
>

Sounds good! I'll take another look on Monday.


libstdc++-v3/ChangeLog: * include/bits/stl_tree.h
> [_GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE]: New macro to control usage of the
> code required to support fancy allocator pointer type. (_Rb_tree_node_
> base::_Const_Base_ptr): Remove. (_Rb_tree_node_base::_S_minimum,
> _Rb_tree_node_base::_S_maximum): Remove overloads for _Const_Base_ptr.
> (_Rb_tree_node_base::_M_base_ptr()): New. (_Rb_tree_node_base::_M_node_ptr<_NodePtr>()):
> New. (_Rb_tree_node::_Link_type): Rename into... (_Rb_tree_node::_Node_ptr):
> ...this. (__rb_tree::_Node_base<>): New. (__rb_tree::_Header<>): New.
> (__rb_tree::_Node<>): New. (_Rb_tree_increment(const
> _Rb_tree_node_base*)): Remove declaration. (_Rb_tree_decrement(const
> _Rb_tree_node_base*)): Remove declaration.
> (_Rb_tree_iterator<>::_Link_type): Rename into...
> (_Rb_tree_iterator<>::_Node_ptr): ...this.
> (_Rb_tree_const_iterator<>::_Link_type): Rename into...
> (_Rb_tree_const_iterator<>::_Node_ptr): ...this.
> (_Rb_tree_const_iterator<>::_M_const_cast): Remove.
> (_Rb_tree_const_iterator<>::_M_node): Change type into _Base_ptr. (__rb_
> tree::_Iterator<>): New. (__rb_tree::_Node_traits<>): New.
> (_Rb_tree<>::_Node_base, _Rb_tree::_Node_type): New.
> (_Rb_tree<>::_Link_type): Rename into... (_Rb_tree<>::_Node_ptr): ...this.
> (_Rb_tree<>::_Const_Base_ptr, _Rb_tree<>::_Const_Node_ptr): Remove.
> (_Rb_tree<>): Adapt to generalize usage of _Base_ptr in place of former
> _Link_type now _Node_ptr. (_Rb_tree<>::_M_mbegin): Remove.
> (_Rb_tree<>::_S_left(_Const_Base_ptr)): Remove.
> (_Rb_tree<>::_S_right(_Const_Base_ptr)): Remove.
> (_Rb_tree<>::_S_maximum(_Const_Base_ptr)): Remove.
> (_Rb_tree<>::_S_minimum(_Const_Base_ptr)): Remove. *
> testsuite/23_containers/map/allocator/ext_ptr.cc: New test case. *
> testsuite/23_containers/multimap/allocator/ext_ptr.cc: New test case. *
> testsuite/23_containers/multiset/allocator/ext_ptr.cc: New test case. *
> testsuite/23_containers/set/allocator/ext_ptr.cc: New test case.
>
> I've tested it in C++98/C++17/C++20 with default
> _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE and with it 9001.
>
> François
> On 18/11/2024 17:39, Jonathan Wakely wrote:
>
> On 18/11/24 06:57 +0100, François Dumont wrote:
>
> Hi
>
> Here is a new proposal with all the cleanup regarding _Const_Base_ptr
> that makes support of allocator's fancy pointer type simpler.
>
> Also submitted as PR:
> https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
>
>   libstdc++: Add fancy pointer support in map and set
>
>    Support fancy allocator pointer type in std::_Rb_tree<>.
>
>    In case of fancy pointer type the container is now storing the
> pointer to
>    _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.
>
>    Many methods are adapted to take and return _Base_ptr in place of
> _Link_type
>    which has been renamed into _Node_ptr.
>
>    As all node are stored as _Base_ptr have all methods working with
> this type
>    and remove _Const_Base_ptr and all methods associated to it.
>
>    libstdc++-v3/ChangeLog:
>
>            * include/bits/stl_set.h (std::set<>::upper_bound<>(const
> _Kt&) const): Fix
>            decltype typo to use const_iterator.
>            * include/bits/stl_tree.h
>            (_Rb_tree_ptr_traits<>): New.
>            (_Rb_tree_pnode_base<>): New.
>            (_Rb_tree_node_base): Inherit from latter.
>            (_Rb_tree_node_base::_Const_Base_ptr): Remove.
>            (_Rb_tree_node_base::_S_minimum(_Const_Base_ptr)): Remove.
>            (_Rb_tree_node_base::_S_maximum(_Const_Base_ptr)): Remove.
>            (_Rb_tree_pheader): New.
>            (_Rb_tree_header): Inherit from latter.
>            (_Rb_tree_node_val): New.
>            (_Rb_tree_node): Inherit from latter.
>            (_Rb_tree_pnode): New.
>            (_Rb_tree_iterator<>::_Link_type): Rename into...
>            (_Rb_tree_iterator<>::_Node_ptr): ...this.
>            (_Rb_tree_const_iterator<>::_Link_type): Rename into...
>            (_Rb_tree_const_iterator<>::_Node_ptr): ...this.
>            (_Rb_tree_const_iterator<>::_M_node): Change type into
> _Base_ptr.
>            (_Rb_tree_const_iterator<>::_M_const_cast): Remove.
>            (_Rb_tree_helpers<>): New.
>            (_Rb_tree_piterator): New.
>            (_Rb_tree_const_piterator): New.
>            (_Rb_tree_node_traits<>): New.
>            (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
>            (_Rb_tree<>::_Const_Base_ptr): Remove.
>            (_Rb_tree): Adapt to generalize usage of _Base_ptr in
> place of _Link_type.
>            (_Rb_tree<>::_M_mbegin): Remove.
>            (_Rb_tree<>::_S_left(_Const_Base_ptr)): Remove.
>            (_Rb_tree<>::_S_right(_Const_Base_ptr)): Remove.
>            (_Rb_tree<>::_S_maximum(_Const_Base_ptr)): Remove.
>            (_Rb_tree<>::_S_minimum(_Const_Base_ptr)): Remove.
>            * testsuite/23_containers/map/allocator/ext_ptr.cc: New
> test case.
>            * testsuite/23_containers/multimap/allocator/ext_ptr.cc:
> New test case.
>            * testsuite/23_containers/multiset/allocator/ext_ptr.cc:
> New test case.
>            * testsuite/23_containers/set/allocator/ext_ptr.cc: New
> test case.
>
> Tested under Linux x64.
>
>
> Note that I've also run the 23_containers tests on map, multimap,
> multiset and set tweaking implementation
> so that new types are being used when C++11 or later even if allocator
> pointer type is a C pointer.
>
> Please take a look at the _GLIBCXX_USE_ALLOC_PTR_xxx macros inhttps://gcc.gnu.org/pipermail/gcc-patches/2024-November/669128.html
> and https://gcc.gnu.org/pipermail/gcc-patches/2024-November/669127.html
> Those macros allow the fancy pointer support to be disabled, in case
> it will break anything for current users. Maybe more importantly, it
> allows the new node types to be used always, even for T*. That means
> we can run the entire testsuite using the new nodes without tweaking
> the implementation.
>
> Please consider a _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE macro (or some
> other name).
>
>
> Ok to commit ?
>
> François
>
> On 12/11/2024 15:56, Jonathan Wakely wrote:
>
> On Mon, 4 Nov 2024 at 21:34, François Dumont <frs.dumont@gmail.com> <frs.dumont@gmail.com> wrote:
>
> On 04/11/2024 19:45, Jonathan Wakely wrote:
>
> On Mon, 4 Nov 2024 at 18:30, François Dumont <frs.dumont@gmail.com> <frs.dumont@gmail.com> wrote:
>
> On 21/10/2024 06:56, François Dumont wrote:
>
>
> On 17/10/2024 23:11, Jonathan Wakely wrote:
>
>
>
> On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely <jwakely.gcc@gmail.com> <jwakely.gcc@gmail.com> wrote:
>
> On Thu, 17 Oct 2024 at 20:52, François Dumont <frs.dumont@gmail.com> <frs.dumont@gmail.com> wrote:
>
> Here is an updated version that compiles, I think, all your feedbacks. It's much cleaner indeed.
>
> Thanks, I'll take a look tomorrow.
>
>
> It's also tested in C++98/17/23.
>
> I'm surprised that we do not need to consider potential allocator::const_pointer.
>
> Do you mean consider the case where Alloc::const_pointer is not the same type as rebinding 'pointer' to a const element type?
>
> Yes, exactly.
>
>
>
> We don't need to consider that because we never get a 'const_pointer' from the allocator, and we never need to pass a 'const_pointer' to the allocator. The allocator's 'allocate' and 'deallocate' members both work with the 'pointer' type, so we only need to use that type when interacting with the allocator. For all the other uses, such as _Const_Node_ptr, what we need is a pointer-to-const that's compatible with the allocator's pointer type. It doesn't actually matter if it's the same type as allocator_traits<Alloc>::const_pointer, because we don't need
>
> Sorry, I sent the email before finishing that thought!
>
> ... we don't need to pass a const_pointer to anything, we only need it for the container's own purposes.
>
> But thinking about it some more, do we even need a const-pointer for the container?  Currently the const_iterator stores a const-pointer, and some members like _M_root() and _M_leftmost() return a const-pointer. But they don't need to. The nodes are all pointed to by a non-const _Base_ptr, none of the storage managed by the container is const.
>
> Except _M_impl._M_header in a const context.
>
> Using const_cast would result in a similar UB that you fixed on _GLIBCXX_DEBUG containers, no ?
>
> We only use a pointer to _M_header for the past-the-end iterator,
> which is not dereferenceable. It's OK to do the const_cast, it's not
> OK to write something through that pointer/reference if the object is
> really const. But you can't write through a past-the-end pointer
> anyway.
>
>
> One last question then to complete this work, is it fine to change
> _Rb_tree_const_iterator::_M_node to _Base_ptr ? No abi issue in doing so ?
>
> I thought I'd sent a reply to this question, but I don't see it in the
> archives, sorry.
>
> So changing from _Const_Base_ptr to _Base_ptr? Yes, I think that's OK.
>
>
> diff --git a/libstdc++-v3/include/bits/stl_set.h b/libstdc++-v3/include/bits/stl_set.h
> index c0eb4dbf65f..3d17626f330 100644
> --- a/libstdc++-v3/include/bits/stl_set.h
> +++ b/libstdc++-v3/include/bits/stl_set.h
> @@ -875,7 +875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>       template<typename _Kt>
>     auto
>     upper_bound(const _Kt& __x) const
> -    -> decltype(iterator(_M_t._M_upper_bound_tr(__x)))
> +    -> decltype(const_iterator(_M_t._M_upper_bound_tr(__x)))
>     { return const_iterator(_M_t._M_upper_bound_tr(__x)); }
> #endif
>       ///@}
> diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
> index bc27e191e8b..c6cba99726d 100644
> --- a/libstdc++-v3/include/bits/stl_tree.h
> +++ b/libstdc++-v3/include/bits/stl_tree.h
> @@ -96,44 +96,100 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>   enum _Rb_tree_color { _S_red = false, _S_black = true };
>
> -  struct _Rb_tree_node_base
> -  {
> -    typedef _Rb_tree_node_base* _Base_ptr;
> -    typedef const _Rb_tree_node_base* _Const_Base_ptr;
> -
> -    _Rb_tree_color    _M_color;
> -    _Base_ptr        _M_parent;
> -    _Base_ptr        _M_left;
> -    _Base_ptr        _M_right;
> -
> -    static _Base_ptr
> -    _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> +#if __cplusplus >= 201103L
> +  template<typename _Ptr>
> +    struct _Rb_tree_ptr_traits
>     {
> -      while (__x->_M_left != 0) __x = __x->_M_left;
> -      return __x;
> -    }
> +      template<typename _Tp>
> +    struct __rebind
> +    { using __type = __ptr_rebind<_Ptr, _Tp>; };
>
> This only seems to be used in one place, can you just use __ptr_rebind
> directly in that one place?
>
>
> +
> +      template<typename _BasePtr>
> +    static _Ptr
> +    _S_ptr_to(_BasePtr __this) noexcept
> +    { return pointer_traits<_Ptr>::pointer_to(*__this); }
>
> This is also only needed in exactly one place. I avoided needing this
> function by adding _M_base_ptr() to the node base classes.
>
> The whole of this _Rb_tree_ptr_traits class template seems unnecessary
> to me. Just inline the code directly into the places it's needed.
> You're not optimizing anything by replacing
> pointer_traits<T*>::pointer_to with
> _Rb_tree_ptr_traits<T*>::_S_ptr_to, it's still a class template
> specialization and a function call.
>
>
> +
> +      template<typename _BasePtr, typename _UpPtr>
> +    static _UpPtr
> +    _S_up_ptr_to(_BasePtr __this) noexcept
>
> pointer_to is not guaranteed to be noexcept.
>
> This appears to be a downcast, i.e. a base-to-derived cast, having
> "up" in the name is a bit confusing.
>
>
> +    {
> +      using __ptr_traits = pointer_traits<_UpPtr>;
> +      using __up_type = typename __ptr_traits::element_type;
> +      auto __up_this = static_cast<__up_type*>(__this);
> +      return __ptr_traits::pointer_to(*__up_this);
> +    }
> +    };
> +#else
> +  template<typename _Ptr>
> +    struct _Rb_tree_ptr_traits;
> +#endif
>
> -    static _Const_Base_ptr
> -    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
> +  template<typename _Tp>
> +    struct _Rb_tree_ptr_traits<_Tp*>
>     {
> -      while (__x->_M_left != 0) __x = __x->_M_left;
> -      return __x;
> -    }
> +      template<typename>
> +    struct __rebind
> +    { typedef _Tp* __type; };
> +
> +      template<typename _BasePtr>
> +    static _Tp*
> +    _S_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
> +    { return static_cast<_Tp*>(__this); }
> +
> +      template<typename _BasePtr, typename _UpPtr>
> +    static _UpPtr
> +    _S_up_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
> +    { return static_cast<_UpPtr>(__this); }
> +    };
>
> -    static _Base_ptr
> -    _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> +  template<typename _BasePtr>
> +    struct _Rb_tree_pnode_base
>     {
> -      while (__x->_M_right != 0) __x = __x->_M_right;
> -      return __x;
> -    }
> +      typedef typename _Rb_tree_ptr_traits<_BasePtr>::template
> +    __rebind<_Rb_tree_pnode_base>::__type _Base_ptr;
>
> The names _BasePtr and _Base_ptr are very similar, and confusing.
> It looks like sometimes the template argument that is substituted for
> _BasePtr is sometimes a derived class (_Rb_tree_node_base) and
> sometimes is _ValPtr. It's never a "base" pointer. Please rename it.
>
>
> -    static _Const_Base_ptr
> -    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
> -    {
> -      while (__x->_M_right != 0) __x = __x->_M_right;
> -      return __x;
> -    }
> -  };
> +      _Base_ptr
> +      _M_self_ptr() _GLIBCXX_NOEXCEPT
>
> Why do we need a non-const overload? The const one will work for all
> cases, right?
>
> I added a comment to my list patches pointing out that it's not
> const-correct, but is safe as long as only used carefully.
>
>
>
> +      { return _Rb_tree_ptr_traits<_Base_ptr>::_S_ptr_to(this); }
> +
> +      _Base_ptr
> +      _M_self_ptr() const _GLIBCXX_NOEXCEPT
> +      {
> +    _Rb_tree_pnode_base* __self = const_cast<_Rb_tree_pnode_base*>(this);
> +    return __self->_M_self_ptr();
> +      }
> +
> +      template<typename _NodePtr>
> +    _NodePtr
> +    _M_node_ptr() _GLIBCXX_NOEXCEPT
> +    {
> +      return _Rb_tree_ptr_traits<_Base_ptr>::template
> +        _S_up_ptr_to<_Rb_tree_pnode_base*, _NodePtr>(this);
> +    }
> +
> +      _Rb_tree_color    _M_color;
> +      _Base_ptr        _M_parent;
> +      _Base_ptr        _M_left;
> +      _Base_ptr        _M_right;
> +
> +      static _Base_ptr
> +      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> +      {
> +    while (__x->_M_left) __x = __x->_M_left;
> +    return __x;
> +      }
> +
> +      static _Base_ptr
> +      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> +      {
> +    while (__x->_M_right) __x = __x->_M_right;
> +    return __x;
> +      }
> +    };
> +
> +  struct _Rb_tree_node_base
> +    : _Rb_tree_pnode_base<_Rb_tree_node_base*>
> +  { };
>
> This approaches means that the existing specializations of RB trees
> all get twice as many base classes. I don't see an ABI problem, and it
> should generate the same code for optimized builds, but increasing the
> inheritance depth and the number of class template instantiations
> gives the compiler more work to do, compiling slower, and generating
> additional debug info (and maybe RTTI?)
>
> Please take a look at how I did it for the linked lists, where
> specializations for non-fancy pointers, e.g. _List_node<T>, are mostly
> unchanged. They don't get any new base classes, just some new typedefs
> and new accessors like:
>
> _Base_ptr _M_base() { return this; }
> const _List_node* _M_base() const { return this; }
>
> The _List_node_traits class template is used by std::list etc. to
> select whether to use _List_node<Alloc::value_type> or
> __list::_Node<Alloc::pointer>. All the fancy pointer support is in the
> new type hierarchy, __list::_Node_base, __list::_Node, and
> __list::_Iterator. The old types like _List_node don't really change.
>
> I found that using the __list namespace with new types reduced a lot
> of confusion I had between the very similar names _List_node and
> _List_pnode (or _List_ptr_node, or other names like that).
>
> Maybe we could call it _List::_Node instead of __list::_Node, so it's
> a bit closer to _List_node but still distinguishable (and would
> produce very slightly shorter mangled names). I didn't do that,
> because our convention is to name impl namespaces as __foo not _Foo.
>
> That pattern could naturally be extended to __tree::_Node (or
> _Tree::_Node) as well.
>
>
>   // Helper type offering value initialization guarantee on the compare functor.
>   template<typename _Key_compare>
> @@ -163,81 +219,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     };
>
>   // Helper type to manage default initialization of node count and header.
> -  struct _Rb_tree_header
> -  {
> -    _Rb_tree_node_base    _M_header;
> -    size_t        _M_node_count; // Keeps track of size of tree.
> -
> -    _Rb_tree_header() _GLIBCXX_NOEXCEPT
> +  template<typename _NodeBase>
> +    struct _Rb_tree_pheader
>     {
> -      _M_header._M_color = _S_red;
> -      _M_reset();
> -    }
> +    private:
> +      typedef typename _NodeBase::_Base_ptr        _Base_ptr;
> +
> +    public:
> +      _NodeBase        _M_header;
> +      size_t        _M_node_count; // Keeps track of size of tree.
> +
> +      _Rb_tree_pheader() _GLIBCXX_NOEXCEPT
> +      {
> +    _M_header._M_color = _S_red;
> +    _M_reset();
> +      }
>
> #if __cplusplus >= 201103L
> -    _Rb_tree_header(_Rb_tree_header&& __x) noexcept
> -    {
> -      if (__x._M_header._M_parent != nullptr)
> -    _M_move_data(__x);
> -      else
> -    {
> -      _M_header._M_color = _S_red;
> -      _M_reset();
> -    }
> -    }
> +      _Rb_tree_pheader(_Rb_tree_pheader&& __x) noexcept
> +      {
> +    if (__x._M_header._M_parent)
> +      _M_move_data(__x);
> +    else
> +      {
> +        _M_header._M_color = _S_red;
> +        _M_reset();
> +      }
> +      }
> #endif
>
> -    void
> -    _M_move_data(_Rb_tree_header& __from)
> -    {
> -      _M_header._M_color = __from._M_header._M_color;
> -      _M_header._M_parent = __from._M_header._M_parent;
> -      _M_header._M_left = __from._M_header._M_left;
> -      _M_header._M_right = __from._M_header._M_right;
> -      _M_header._M_parent->_M_parent = &_M_header;
> -      _M_node_count = __from._M_node_count;
> -
> -      __from._M_reset();
> -    }
> +      void
> +      _M_move_data(_Rb_tree_pheader& __from)
> +      {
> +    _M_header._M_color = __from._M_header._M_color;
> +    _M_header._M_parent = __from._M_header._M_parent;
> +    _M_header._M_left = __from._M_header._M_left;
> +    _M_header._M_right = __from._M_header._M_right;
> +    _M_header._M_parent->_M_parent = _M_header._M_self_ptr();
> +    _M_node_count = __from._M_node_count;
> +
> +    __from._M_reset();
> +      }
>
> -    void
> -    _M_reset()
> -    {
> -      _M_header._M_parent = 0;
> -      _M_header._M_left = &_M_header;
> -      _M_header._M_right = &_M_header;
> -      _M_node_count = 0;
> -    }
> -  };
> +      void
> +      _M_reset()
> +      {
> +    _M_header._M_parent = _Base_ptr();
> +    _M_header._M_left = _M_header._M_right = _M_header._M_self_ptr();
> +    _M_node_count = 0;
> +      }
> +    };
> +
> +  struct _Rb_tree_header : _Rb_tree_pheader<_Rb_tree_node_base>
> +  { };
>
>   template<typename _Val>
> -    struct _Rb_tree_node : public _Rb_tree_node_base
> +    struct _Rb_tree_node_val
>     {
> -      typedef _Rb_tree_node<_Val>* _Link_type;
> -
> #if __cplusplus < 201103L
>       _Val _M_value_field;
>
> +      __attribute__((__always_inline__))
>       _Val*
>       _M_valptr()
>       { return std::__addressof(_M_value_field); }
>
> +      __attribute__((__always_inline__))
>       const _Val*
>       _M_valptr() const
>       { return std::__addressof(_M_value_field); }
> #else
>       __gnu_cxx::__aligned_membuf<_Val> _M_storage;
>
> For the new __list::_Node type I took the opportunity to replace
> __aligned_membuf with a union { _Val _M_data; }; which is simpler and
> produces the same code. We could probably make that change for the
> existing node types, I think it's safe, but making it only for the new
> _Node type means the old node type isn't affected.
>
>
>
> +      [[__gnu__::__always_inline__]]
>       _Val*
>       _M_valptr()
>       { return _M_storage._M_ptr(); }
>
> +      [[__gnu__::__always_inline__]]
>       const _Val*
>       _M_valptr() const
>       { return _M_storage._M_ptr(); }
> #endif
>     };
>
> +  template<typename _Val>
> +    struct _Rb_tree_node
> +    : _Rb_tree_node_base
> +    , _Rb_tree_node_val<_Val>
> +    {
> +      typedef _Rb_tree_node<_Val>* _Node_ptr;
> +    };
> +
> +#if __cplusplus >= 201103L
> +  template<typename _ValPtr>
> +    struct _Rb_tree_pnode
> +    : _Rb_tree_pnode_base<_ValPtr>
>
> This makes _Rb_tree_pnode_base depend on the value_type, which means
> you instantiate a different specialization of _Rb_tree_pnode_base for
> every _Rb_tree. There's no point even having a common base class if
> every specialization is different. You might as well just put the
> _M_parent/_M_left/_M_right pointers in _Rb_tree_pnode directly.
>
> The point of the node_base class is to have a common base that is not
> dependent on the value type, so that std::set<int> and std::set<long>
> and std::set<int, Cmp> all use the same node base. The way this patch
> works, std::set<int> will use _Rb_tree_pnode_base<int*> andstd::set<long> will use _Rb_tree_pnode_base<long*>.
>
> This is why my patch for lists uses __ptr_rebind<_ValPtr, void> as the
> template argument to _Node_base, so that they all use
> _Node_base<void*>.
>
> Obviously for a fancy pointer, it will be a different specialization,
> so std::list<int, A<int>> uses _Node_base<A::void_pointer>, butstd::list<long, A<long>> uses the same _Node_base<A::void_pointer>.
>
>
> +    , _Rb_tree_node_val<typename std::pointer_traits<_ValPtr>::element_type>
> +    {
> +      using _Node_ptr = __ptr_rebind<_ValPtr, _Rb_tree_pnode>;
> +    };
> +#endif
> +
>   _GLIBCXX_PURE _Rb_tree_node_base*
>   _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
>
>
> [...]
>
>
> +#if __cplusplus >= 201103L
> +  template<typename _ValPtr>
> +    struct _Rb_tree_piterator
> +    {
> +      using __ptr_traits = pointer_traits<_ValPtr>;
> +      using value_type = typename __ptr_traits::element_type;
> +      using reference = value_type&;
> +      using pointer = value_type*;
> +
> +      typedef bidirectional_iterator_tag iterator_category;
> +      typedef ptrdiff_t             difference_type;
> +
> +      typedef _Rb_tree_piterator            _Self;
> +      typedef _Rb_tree_pnode_base<_ValPtr>        _Node_base;
> +      typedef _Rb_tree_pnode<_ValPtr>            _Node_type;
> +      typedef typename _Node_type::_Base_ptr        _Base_ptr;
> +      typedef typename _Node_type::_Node_ptr        _Node_ptr;
> +      typedef _Rb_tree_helpers<_Node_base>        _Tree_helpers;
> +
> +      _Rb_tree_piterator() _GLIBCXX_NOEXCEPT
> +      : _M_node() { }
> +
> +      explicit
> +      _Rb_tree_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> +      : _M_node(__x) { }
> +
> +      reference
> +      operator*() const _GLIBCXX_NOEXCEPT
> +      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
> +
> +      pointer
> +      operator->() const _GLIBCXX_NOEXCEPT
> +      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
> +
> +      _Self&
> +      operator++() _GLIBCXX_NOEXCEPT
> +      {
> +    _M_node = _Tree_helpers::_Increment(_M_node);
> +    return *this;
> +      }
> +
> +      _Self
> +      operator++(int) _GLIBCXX_NOEXCEPT
> +      {
> +    _Self __tmp = *this;
> +    _M_node = _Tree_helpers::_Increment(_M_node);
> +    return __tmp;
> +      }
> +
> +      _Self&
> +      operator--() _GLIBCXX_NOEXCEPT
> +      {
> +    _M_node = _Tree_helpers::_Decrement(_M_node);
> +    return *this;
> +      }
> +
> +      _Self
> +      operator--(int) _GLIBCXX_NOEXCEPT
> +      {
> +    _Self __tmp = *this;
> +    _M_node = _Tree_helpers::_Decrement(_M_node);
> +    return __tmp;
> +      }
> +
> +      friend bool
> +      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
> +      { return __x._M_node == __y._M_node; }
> +
> +#if ! __cpp_lib_three_way_comparison
> +      friend bool
> +      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
> +      { return __x._M_node != __y._M_node; }
> +#endif
> +
> +      _Base_ptr _M_node;
> +    };
> +
> +  template<typename _ValPtr>
> +    struct _Rb_tree_const_piterator
>
> For the linked lists, I didn't add two new iterator types, just
> _Iterator<_Const, _ValPtr> which is used for iterator and
> const_iterator.
>
> It seemed nice not to duplicate the iterator type, since the iterator
> and const_iterator are 95% the same. If we don't need to support
> C++98, we can write a single iterator implementation.
>
> If you have a single iterator implementation, you probably don't need
> the _Rb_tree_helpers.
>
> There would still be uses of insert-and-rebalance and
> rebalance-for-erase, but those could just be new overloads for fancy
> pointers. I don't think we need the _Rb_tree_helpers class template.
>
>
>
> +    {
> +      using __ptr_traits = pointer_traits<_ValPtr>;
> +      using value_type = typename __ptr_traits::element_type;
> +      using reference = const value_type&;
> +      using pointer = const value_type*;
> +
> +      typedef _Rb_tree_piterator<_ValPtr>        iterator;
> +      typedef typename iterator::_Base_ptr        _Base_ptr;
> +
> +      typedef bidirectional_iterator_tag iterator_category;
> +      typedef ptrdiff_t             difference_type;
> +
> +      typedef _Rb_tree_const_piterator            _Self;
> +      typedef _Rb_tree_pnode_base<_ValPtr>        _Node_base;
> +      typedef _Rb_tree_pnode<_ValPtr>            _Node_type;
> +      typedef _Rb_tree_helpers<_Node_base>        _Tree_helpers;
> +
> +      _Rb_tree_const_piterator() _GLIBCXX_NOEXCEPT
> +      : _M_node() { }
> +
> +      explicit
> +      _Rb_tree_const_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> +      : _M_node(__x) { }
> +
> +      _Rb_tree_const_piterator(const iterator& __it) _GLIBCXX_NOEXCEPT
> +      : _M_node(__it._M_node) { }
> +
> +      reference
> +      operator*() const _GLIBCXX_NOEXCEPT
> +      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
> +
> +      pointer
> +      operator->() const _GLIBCXX_NOEXCEPT
> +      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
> +
> +      _Self&
> +      operator++() _GLIBCXX_NOEXCEPT
> +      {
> +    _M_node = _Tree_helpers::_Increment(_M_node);
>
> You need the iterator increment code here, but you can define it
> inline without the _Increment helper.
>
>
> +    return *this;
> +      }
> +
> +      _Self
> +      operator++(int) _GLIBCXX_NOEXCEPT
> +      {
> +    _Self __tmp = *this;
> +    _M_node = _Tree_helpers::_Increment(_M_node);
>
> This could just use ++*this. So if there's no separate const iterator
> implementation, there's no more use of _Increment.
>
>
> +    return __tmp;
> +      }
> +
> +      _Self&
> +      operator--() _GLIBCXX_NOEXCEPT
> +      {
> +    _M_node = _Tree_helpers::_Decrement(_M_node);
> +    return *this;
> +      }
> +
> +      _Self
> +      operator--(int) _GLIBCXX_NOEXCEPT
> +      {
> +    _Self __tmp = *this;
> +    _M_node = _Tree_helpers::_Decrement(_M_node);
> +    return __tmp;
> +      }
> +
> +      friend bool
> +      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
> +      { return __x._M_node == __y._M_node; }
> +
> +#if ! __cpp_lib_three_way_comparison
> +      friend bool
> +      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
> +      { return __x._M_node != __y._M_node; }
> +#endif
> +
> +      _Base_ptr _M_node;
> +    };
> +#endif // C++11
> +
> +#if __cplusplus >= 201103L
> +  template<typename _ValPtr>
> +    struct _Rb_tree_node_traits
> +    {
> +      using _Node_base        = _Rb_tree_pnode_base<_ValPtr>;
> +      using _Node_type        = _Rb_tree_pnode<_ValPtr>;
> +      using _Header_t        = _Rb_tree_pheader<_Node_base>;
> +      using _Iterator_t        = _Rb_tree_piterator<_ValPtr>;
> +      using _Const_iterator_t    = _Rb_tree_const_piterator<_ValPtr>;
> +    };
> +#else
> +  template<typename _ValPtr>
> +    struct _Rb_tree_node_traits;
> +#endif
> +
> +  template<typename _Val>
> +    struct _Rb_tree_node_traits<_Val*>
> +    {
> +      typedef _Rb_tree_node_base        _Node_base;
> +      typedef _Rb_tree_node<_Val>        _Node_type;
> +      typedef _Rb_tree_header            _Header_t;
> +      typedef _Rb_tree_iterator<_Val>        _Iterator_t;
> +      typedef _Rb_tree_const_iterator<_Val>    _Const_iterator_t;
> +    };
>
> #if __cplusplus > 201402L
>   template<typename _Tree1, typename _Cmp2>
> @@ -424,16 +1068,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        typename _Compare, typename _Alloc = allocator<_Val> >
>     class _Rb_tree
>     {
> +      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::pointer _ValPtr;
> +      typedef _Rb_tree_node_traits<_ValPtr> _Node_traits;
> +
> +      typedef typename _Node_traits::_Node_base        _Node_base;
> +      typedef typename _Node_traits::_Node_type        _Node_type;
> +
>       typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
> -    rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
> +    rebind<_Node_type>::other _Node_allocator;
>
>       typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
>
>     protected:
> -      typedef _Rb_tree_node_base*         _Base_ptr;
> -      typedef const _Rb_tree_node_base*     _Const_Base_ptr;
> -      typedef _Rb_tree_node<_Val>*         _Link_type;
> -      typedef const _Rb_tree_node<_Val>*    _Const_Link_type;
> +      typedef typename _Node_base::_Base_ptr         _Base_ptr;
> +      typedef typename _Node_type::_Node_ptr         _Node_ptr;
>
> Accessing a member of _Node_type here requires _Node_type to be
> complete, which requires _Tp to be complete. That's allowed by the
> standard, because std::map and std::set don't support incomplete types
> (unlike vector and the linked lists).
>
> But we don't really ned to instantiate _Node_type here, we could use
> _Alloc_traits::pointer instead.
>
>
> [...]
>
>
> @@ -1826,10 +2427,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>                   || _M_impl._M_key_compare(_KeyOfValue()(__v),
>                             _S_key(__p)));
>
> -    _Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
> +    _Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
>
> I think this relies on implicit conversion from _Node_ptr to
> _Base_ptr, which is not necessarily possible. It works for the
> _Pointer_adapter type in <ext/pointer.h> because it has a
> template<typename U> _Pointer_adapter(_Pointer_adapter<U>) constructor
> that supports implicit conversions.
>
> There seems to be a few other places with this problem. Please take a
> look at the Pointer type in the new test for my list and forward_list
> fancy ptr patches (that Pointer should probably be added to the
> testsuite/util/testsuite_allocator.h header for reuse).
>
> I solved these problems by adding a _M_base_ptr() function to the
> node_base type, which means that instead of relying on an implicit
> conversion from node_ptr to _Base_ptr you call node_ptr->_M_base_ptr()
> to get a _Base_ptr.
>
>
> @@ -2255,7 +2821,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     }
>       else
>     // Equivalent keys.
> -    return _Res(__pos._M_node, 0);
> +    return _Res(__position._M_node, 0);
>     }
>
>   template<typename _Key, typename _Val, typename _KeyOfValue,
> @@ -2294,11 +2860,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>     _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
>     {
> -      iterator __pos = __position._M_const_cast();
>
> This removes __pos ...
>
>
>       typedef pair<_Base_ptr, _Base_ptr> _Res;
>
>       // end()
> -      if (__pos._M_node == _M_end())
> +      if (__position._M_node == _M_end())
>     {
>       if (size() > 0
>           && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
> @@ -2306,18 +2871,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       else
>         return _M_get_insert_equal_pos(__k);
>     }
> -      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
> +      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
>     {
>       // First, try before...
>       iterator __before = __pos;
>
> But it's still used here.
>
>
> -      if (__pos._M_node == _M_leftmost()) // begin()
> +      if (__position._M_node == _M_leftmost()) // begin()
>         return _Res(_M_leftmost(), _M_leftmost());
>       else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
>         {
>           if (_S_right(__before._M_node) == 0)
>         return _Res(0, __before._M_node);
>           else
> -        return _Res(__pos._M_node, __pos._M_node);
> +        return _Res(__position._M_node, __position._M_node);
>         }
>       else
>         return _M_get_insert_equal_pos(__k);
> @@ -2326,12 +2891,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     {
>       // ... then try after.
>       iterator __after = __pos;
>
> And here.
>
> I think they should both be __position._M_const_cast().
>
>
>
> diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
> new file mode 100644
> index 00000000000..758beedccfd
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
> @@ -0,0 +1,44 @@
> +// { dg-do run { target c++11 } }
> +
> +#include <map>
> +#include <memory>
> +#include <testsuite_hooks.h>
> +#include <testsuite_allocator.h>
> +
> +struct T { int i; };
> +
> +bool operator<(const T& l, const T& r)
> +{ return l.i < r.i; }
> +
> +struct H
>
> This struct H occurs in several tests but seems to be unused.
>
>
> +{
> +  std::size_t
> +  operator()(const T& t) const noexcept
> +  { return t.i; }
> +};
> +
> +struct L : std::less<T>
> +{ };
> +
> +using __gnu_test::CustomPointerAlloc;
> +
> +template class std::map<T, int, L,
> +            CustomPointerAlloc<std::pair<const T, int>>>;
> +
> +void test01()
> +{
> +  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
> +  typedef std::map<T, int, L, alloc_type> test_type;
> +  test_type v;
> +  v.insert({ T(), 0 });
> +  VERIFY( v.begin()->second == 0 );
> +  VERIFY( ++v.begin() == v.end() );
> +  v.insert({ T { 1 }, 1 });
> +  VERIFY( v.size() == 2 );
> +  VERIFY( v.find(T()) != v.end() );
> +}
> +
> +int main()
> +{
> +  test01();
> +}
>
> I'd be interested on your opinion on my patches for fancy pointer
> support in the linked lists. I think it's cleaner and requires fewer
> changes to the existing specializations.
>
> My changes can also be disabled by a macro (to avoid breaking any
> users who are somehow managing to use allocators-with-fancy-pointers
> with RB trees today, which could be useful unless that's completely
> broken today), and to be enabled for non-fancy pointers (for test
> coverage).
>
>
>
  
François Dumont Dec. 4, 2024, 6:27 p.m. UTC | #16
Hi

I've completed the synchronization with your equivalent PR for std::list 
so here is the updated patch.

PR updated a couple of days ago.

Note that I've started to rework the patch for the same in _Hashtable.

François


On 30/11/2024 18:21, Jonathan Wakely wrote:
>
>
> On Sat, 30 Nov 2024, 13:52 François Dumont, <frs.dumont@gmail.com> wrote:
>
>     Hi
>
>     I've applied all your comments below and the ones you did on the
>     PR directly.
>
>     When all new types totally seperated from the legacy types
>     _Rb_tree_node_traits is indeed useless.
>
>     Regarding _Rb_tree_helpers I got rid of it but move the
>     insert-and-rebalance and rebalance-for-erase function to
>     _Node_traits<>.
>
>     PR updated:
>
>     https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
>
>     libstdc++: Add fancy pointer support in map and set Support fancy
>     allocator pointer type in std::_Rb_tree<>. In case of fancy
>     pointer type the container is now storing the pointer to
>     __rb_tree::_Node<_ValPtr> as a pointer to
>     __rb_tree::_Node_base<_VoidPtr>. Many methods are adapted to take
>     and return _Base_ptr in place of _Link_type which has been renamed
>     into _Node_ptr. As all node are stored as _Base_ptr have all
>     methods working with this type and remove _Const_Base_ptr and
>     _Const_Node_ptr and all methods associated with it.
>
>
> Sounds good! I'll take another look on Monday.
>
>
>     libstdc++-v3/ChangeLog: * include/bits/stl_tree.h
>     [_GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE]: New macro to control usage
>     of the code required to support fancy allocator pointer type.
>     (_Rb_tree_node_base::_Const_Base_ptr): Remove.
>     (_Rb_tree_node_base::_S_minimum, _Rb_tree_node_base::_S_maximum):
>     Remove overloads for _Const_Base_ptr.
>     (_Rb_tree_node_base::_M_base_ptr()): New.
>     (_Rb_tree_node_base::_M_node_ptr<_NodePtr>()): New.
>     (_Rb_tree_node::_Link_type): Rename into...
>     (_Rb_tree_node::_Node_ptr): ...this. (__rb_tree::_Node_base<>):
>     New. (__rb_tree::_Header<>): New. (__rb_tree::_Node<>): New.
>     (_Rb_tree_increment(const _Rb_tree_node_base*)): Remove
>     declaration. (_Rb_tree_decrement(const _Rb_tree_node_base*)):
>     Remove declaration. (_Rb_tree_iterator<>::_Link_type): Rename
>     into... (_Rb_tree_iterator<>::_Node_ptr): ...this.
>     (_Rb_tree_const_iterator<>::_Link_type): Rename into...
>     (_Rb_tree_const_iterator<>::_Node_ptr): ...this.
>     (_Rb_tree_const_iterator<>::_M_const_cast): Remove.
>     (_Rb_tree_const_iterator<>::_M_node): Change type into _Base_ptr.
>     (__rb_tree::_Iterator<>): New. (__rb_tree::_Node_traits<>): New.
>     (_Rb_tree<>::_Node_base, _Rb_tree::_Node_type): New.
>     (_Rb_tree<>::_Link_type): Rename into... (_Rb_tree<>::_Node_ptr):
>     ...this. (_Rb_tree<>::_Const_Base_ptr,
>     _Rb_tree<>::_Const_Node_ptr): Remove. (_Rb_tree<>): Adapt to
>     generalize usage of _Base_ptr in place of former _Link_type now
>     _Node_ptr. (_Rb_tree<>::_M_mbegin): Remove.
>     (_Rb_tree<>::_S_left(_Const_Base_ptr)): Remove.
>     (_Rb_tree<>::_S_right(_Const_Base_ptr)): Remove.
>     (_Rb_tree<>::_S_maximum(_Const_Base_ptr)): Remove.
>     (_Rb_tree<>::_S_minimum(_Const_Base_ptr)): Remove. *
>     testsuite/23_containers/map/allocator/ext_ptr.cc: New test case. *
>     testsuite/23_containers/multimap/allocator/ext_ptr.cc: New test
>     case. * testsuite/23_containers/multiset/allocator/ext_ptr.cc: New
>     test case. * testsuite/23_containers/set/allocator/ext_ptr.cc: New
>     test case.
>
>     I've tested it in C++98/C++17/C++20 with default
>     _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE and with it 9001.
>
>     François
>
>     On 18/11/2024 17:39, Jonathan Wakely wrote:
>>     On 18/11/24 06:57 +0100, François Dumont wrote:
>>>     Hi
>>>
>>>     Here is a new proposal with all the cleanup regarding _Const_Base_ptr
>>>     that makes support of allocator's fancy pointer type simpler.
>>>
>>>     Also submitted as PR:
>>>
>>>     https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
>>>
>>>        libstdc++: Add fancy pointer support in map and set
>>>
>>>         Support fancy allocator pointer type instd::_Rb_tree<>.
>>>
>>>         In case of fancy pointer type the container is now storing the
>>>     pointer to
>>>         _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.
>>>
>>>         Many methods are adapted to take and return _Base_ptr in place of
>>>     _Link_type
>>>         which has been renamed into _Node_ptr.
>>>
>>>         As all node are stored as _Base_ptr have all methods working with
>>>     this type
>>>         and remove _Const_Base_ptr and all methods associated to it.
>>>
>>>         libstdc++-v3/ChangeLog:
>>>
>>>                 * include/bits/stl_set.h (std::set<>::upper_bound<>(const
>>>     _Kt&) const): Fix
>>>                 decltype typo to use const_iterator.
>>>                 * include/bits/stl_tree.h
>>>                 (_Rb_tree_ptr_traits<>): New.
>>>                 (_Rb_tree_pnode_base<>): New.
>>>                 (_Rb_tree_node_base): Inherit from latter.
>>>                 (_Rb_tree_node_base::_Const_Base_ptr): Remove.
>>>                 (_Rb_tree_node_base::_S_minimum(_Const_Base_ptr)): Remove.
>>>                 (_Rb_tree_node_base::_S_maximum(_Const_Base_ptr)): Remove.
>>>                 (_Rb_tree_pheader): New.
>>>                 (_Rb_tree_header): Inherit from latter.
>>>                 (_Rb_tree_node_val): New.
>>>                 (_Rb_tree_node): Inherit from latter.
>>>                 (_Rb_tree_pnode): New.
>>>                 (_Rb_tree_iterator<>::_Link_type): Rename into...
>>>                 (_Rb_tree_iterator<>::_Node_ptr): ...this.
>>>                 (_Rb_tree_const_iterator<>::_Link_type): Rename into...
>>>                 (_Rb_tree_const_iterator<>::_Node_ptr): ...this.
>>>                 (_Rb_tree_const_iterator<>::_M_node): Change type into
>>>     _Base_ptr.
>>>                 (_Rb_tree_const_iterator<>::_M_const_cast): Remove.
>>>                 (_Rb_tree_helpers<>): New.
>>>                 (_Rb_tree_piterator): New.
>>>                 (_Rb_tree_const_piterator): New.
>>>                 (_Rb_tree_node_traits<>): New.
>>>                 (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
>>>                 (_Rb_tree<>::_Const_Base_ptr): Remove.
>>>                 (_Rb_tree): Adapt to generalize usage of _Base_ptr in
>>>     place of _Link_type.
>>>                 (_Rb_tree<>::_M_mbegin): Remove.
>>>                 (_Rb_tree<>::_S_left(_Const_Base_ptr)): Remove.
>>>                 (_Rb_tree<>::_S_right(_Const_Base_ptr)): Remove.
>>>                 (_Rb_tree<>::_S_maximum(_Const_Base_ptr)): Remove.
>>>                 (_Rb_tree<>::_S_minimum(_Const_Base_ptr)): Remove.
>>>                 * testsuite/23_containers/map/allocator/ext_ptr.cc: New
>>>     test case.
>>>                 * testsuite/23_containers/multimap/allocator/ext_ptr.cc:
>>>     New test case.
>>>                 * testsuite/23_containers/multiset/allocator/ext_ptr.cc:
>>>     New test case.
>>>                 * testsuite/23_containers/set/allocator/ext_ptr.cc: New
>>>     test case.
>>>
>>>     Tested under Linux x64.
>>>
>>>
>>>     Note that I've also run the 23_containers tests on map, multimap,
>>>     multiset and set tweaking implementation
>>>     so that new types are being used when C++11 or later even if allocator
>>>     pointer type is a C pointer.
>>     Please take a look at the _GLIBCXX_USE_ALLOC_PTR_xxx macros in
>>     https://gcc.gnu.org/pipermail/gcc-patches/2024-November/669128.html
>>     andhttps://gcc.gnu.org/pipermail/gcc-patches/2024-November/669127.html
>>     Those macros allow the fancy pointer support to be disabled, in case
>>     it will break anything for current users. Maybe more importantly, it
>>     allows the new node types to be used always, even for T*. That means
>>     we can run the entire testsuite using the new nodes without tweaking
>>     the implementation.
>>
>>     Please consider a _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE macro (or some
>>     other name).
>>
>>>     Ok to commit ?
>>>
>>>     François
>>>
>>>     On 12/11/2024 15:56, Jonathan Wakely wrote:
>>>>     On Mon, 4 Nov 2024 at 21:34, François Dumont<frs.dumont@gmail.com> <mailto:frs.dumont@gmail.com> wrote:
>>>>>     On 04/11/2024 19:45, Jonathan Wakely wrote:
>>>>>>     On Mon, 4 Nov 2024 at 18:30, François Dumont<frs.dumont@gmail.com> <mailto:frs.dumont@gmail.com> wrote:
>>>>>>>     On 21/10/2024 06:56, François Dumont wrote:
>>>>>>>
>>>>>>>
>>>>>>>     On 17/10/2024 23:11, Jonathan Wakely wrote:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>     On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely<jwakely.gcc@gmail.com> <mailto:jwakely.gcc@gmail.com> wrote:
>>>>>>>>     On Thu, 17 Oct 2024 at 20:52, François Dumont<frs.dumont@gmail.com> <mailto:frs.dumont@gmail.com> wrote:
>>>>>>>>>     Here is an updated version that compiles, I think, all your feedbacks. It's much cleaner indeed.
>>>>>>>>     Thanks, I'll take a look tomorrow.
>>>>>>>>
>>>>>>>>>     It's also tested in C++98/17/23.
>>>>>>>>>
>>>>>>>>>     I'm surprised that we do not need to consider potentialallocator::const_pointer.
>>>>>>>>     Do you mean consider the case whereAlloc::const_pointer is not the same type as rebinding 'pointer' to a const element type?
>>>>>>>     Yes, exactly.
>>>>>>>
>>>>>>>
>>>>>>>>     We don't need to consider that because we never get a 'const_pointer' from the allocator, and we never need to pass a 'const_pointer' to the allocator. The allocator's 'allocate' and 'deallocate' members both work with the 'pointer' type, so we only need to use that type when interacting with the allocator. For all the other uses, such as _Const_Node_ptr, what we need is a pointer-to-const that's compatible with the allocator's pointer type. It doesn't actually matter if it's the same type as allocator_traits<Alloc>::const_pointer, because we don't need
>>>>>>>     Sorry, I sent the email before finishing that thought!
>>>>>>>
>>>>>>>     ... we don't need to pass a const_pointer to anything, we only need it for the container's own purposes.
>>>>>>>
>>>>>>>     But thinking about it some more, do we even need a const-pointer for the container?  Currently the const_iterator stores a const-pointer, and some members like _M_root() and _M_leftmost() return a const-pointer. But they don't need to. The nodes are all pointed to by a non-const _Base_ptr, none of the storage managed by the container is const.
>>>>>>>
>>>>>>>     Except _M_impl._M_header in a const context.
>>>>>>>
>>>>>>>     Using const_cast would result in a similar UB that you fixed on _GLIBCXX_DEBUG containers, no ?
>>>>>>     We only use a pointer to _M_header for the past-the-end iterator,
>>>>>>     which is not dereferenceable. It's OK to do the const_cast, it's not
>>>>>>     OK to write something through that pointer/reference if the object is
>>>>>>     really const. But you can't write through a past-the-end pointer
>>>>>>     anyway.
>>>>>>
>>>>>     One last question then to complete this work, is it fine to change
>>>>>     _Rb_tree_const_iterator::_M_node to _Base_ptr ? No abi issue in doing so ?
>>>>     I thought I'd sent a reply to this question, but I don't see it in the
>>>>     archives, sorry.
>>>>
>>>>     So changing from _Const_Base_ptr to _Base_ptr? Yes, I think that's OK.
>>>>
>>>     diff --git a/libstdc++-v3/include/bits/stl_set.h b/libstdc++-v3/include/bits/stl_set.h
>>>     index c0eb4dbf65f..3d17626f330 100644
>>>     --- a/libstdc++-v3/include/bits/stl_set.h
>>>     +++ b/libstdc++-v3/include/bits/stl_set.h
>>>     @@ -875,7 +875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>            template<typename _Kt>
>>>          auto
>>>          upper_bound(const _Kt& __x) const
>>>     -    -> decltype(iterator(_M_t._M_upper_bound_tr(__x)))
>>>     +    -> decltype(const_iterator(_M_t._M_upper_bound_tr(__x)))
>>>          { return const_iterator(_M_t._M_upper_bound_tr(__x)); }
>>>     #endif
>>>            ///@}
>>>     diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
>>>     index bc27e191e8b..c6cba99726d 100644
>>>     --- a/libstdc++-v3/include/bits/stl_tree.h
>>>     +++ b/libstdc++-v3/include/bits/stl_tree.h
>>>     @@ -96,44 +96,100 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>
>>>        enum _Rb_tree_color { _S_red = false, _S_black = true };
>>>
>>>     -  struct _Rb_tree_node_base
>>>     -  {
>>>     -    typedef _Rb_tree_node_base* _Base_ptr;
>>>     -    typedef const _Rb_tree_node_base* _Const_Base_ptr;
>>>     -
>>>     -    _Rb_tree_color    _M_color;
>>>     -    _Base_ptr        _M_parent;
>>>     -    _Base_ptr        _M_left;
>>>     -    _Base_ptr        _M_right;
>>>     -
>>>     -    static _Base_ptr
>>>     -    _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>     +#if __cplusplus >= 201103L
>>>     +  template<typename _Ptr>
>>>     +    struct _Rb_tree_ptr_traits
>>>          {
>>>     -      while (__x->_M_left != 0) __x = __x->_M_left;
>>>     -      return __x;
>>>     -    }
>>>     +      template<typename _Tp>
>>>     +    struct __rebind
>>>     +    { using __type = __ptr_rebind<_Ptr, _Tp>; };
>>     This only seems to be used in one place, can you just use __ptr_rebind
>>     directly in that one place?
>>
>>>     +
>>>     +      template<typename _BasePtr>
>>>     +    static _Ptr
>>>     +    _S_ptr_to(_BasePtr __this) noexcept
>>>     +    { return pointer_traits<_Ptr>::pointer_to(*__this); }
>>     This is also only needed in exactly one place. I avoided needing this
>>     function by adding _M_base_ptr() to the node base classes.
>>
>>     The whole of this _Rb_tree_ptr_traits class template seems unnecessary
>>     to me. Just inline the code directly into the places it's needed.
>>     You're not optimizing anything by replacing
>>     pointer_traits<T*>::pointer_to with
>>     _Rb_tree_ptr_traits<T*>::_S_ptr_to, it's still a class template
>>     specialization and a function call.
>>
>>>     +
>>>     +      template<typename _BasePtr, typename _UpPtr>
>>>     +    static _UpPtr
>>>     +    _S_up_ptr_to(_BasePtr __this) noexcept
>>     pointer_to is not guaranteed to be noexcept.
>>
>>     This appears to be a downcast, i.e. a base-to-derived cast, having
>>     "up" in the name is a bit confusing.
>>
>>>     +    {
>>>     +      using __ptr_traits = pointer_traits<_UpPtr>;
>>>     +      using __up_type = typename __ptr_traits::element_type;
>>>     +      auto __up_this = static_cast<__up_type*>(__this);
>>>     +      return __ptr_traits::pointer_to(*__up_this);
>>>     +    }
>>>     +    };
>>>     +#else
>>>     +  template<typename _Ptr>
>>>     +    struct _Rb_tree_ptr_traits;
>>>     +#endif
>>>
>>>     -    static _Const_Base_ptr
>>>     -    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>     +  template<typename _Tp>
>>>     +    struct _Rb_tree_ptr_traits<_Tp*>
>>>          {
>>>     -      while (__x->_M_left != 0) __x = __x->_M_left;
>>>     -      return __x;
>>>     -    }
>>>     +      template<typename>
>>>     +    struct __rebind
>>>     +    { typedef _Tp* __type; };
>>>     +
>>>     +      template<typename _BasePtr>
>>>     +    static _Tp*
>>>     +    _S_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
>>>     +    { return static_cast<_Tp*>(__this); }
>>>     +
>>>     +      template<typename _BasePtr, typename _UpPtr>
>>>     +    static _UpPtr
>>>     +    _S_up_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
>>>     +    { return static_cast<_UpPtr>(__this); }
>>>     +    };
>>>
>>>     -    static _Base_ptr
>>>     -    _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>     +  template<typename _BasePtr>
>>>     +    struct _Rb_tree_pnode_base
>>>          {
>>>     -      while (__x->_M_right != 0) __x = __x->_M_right;
>>>     -      return __x;
>>>     -    }
>>>     +      typedef typename _Rb_tree_ptr_traits<_BasePtr>::template
>>>     +    __rebind<_Rb_tree_pnode_base>::__type _Base_ptr;
>>     The names _BasePtr and _Base_ptr are very similar, and confusing.
>>     It looks like sometimes the template argument that is substituted for
>>     _BasePtr is sometimes a derived class (_Rb_tree_node_base) and
>>     sometimes is _ValPtr. It's never a "base" pointer. Please rename it.
>>
>>>     -    static _Const_Base_ptr
>>>     -    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>     -    {
>>>     -      while (__x->_M_right != 0) __x = __x->_M_right;
>>>     -      return __x;
>>>     -    }
>>>     -  };
>>>     +      _Base_ptr
>>>     +      _M_self_ptr() _GLIBCXX_NOEXCEPT
>>     Why do we need a non-const overload? The const one will work for all
>>     cases, right?
>>
>>     I added a comment to my list patches pointing out that it's not
>>     const-correct, but is safe as long as only used carefully.
>>
>>
>>>     +      { return _Rb_tree_ptr_traits<_Base_ptr>::_S_ptr_to(this); }
>>>     +
>>>     +      _Base_ptr
>>>     +      _M_self_ptr() const _GLIBCXX_NOEXCEPT
>>>     +      {
>>>     +    _Rb_tree_pnode_base* __self = const_cast<_Rb_tree_pnode_base*>(this);
>>>     +    return __self->_M_self_ptr();
>>>     +      }
>>>     +
>>>     +      template<typename _NodePtr>
>>>     +    _NodePtr
>>>     +    _M_node_ptr() _GLIBCXX_NOEXCEPT
>>>     +    {
>>>     +      return _Rb_tree_ptr_traits<_Base_ptr>::template
>>>     +        _S_up_ptr_to<_Rb_tree_pnode_base*, _NodePtr>(this);
>>>     +    }
>>>     +
>>>     +      _Rb_tree_color    _M_color;
>>>     +      _Base_ptr        _M_parent;
>>>     +      _Base_ptr        _M_left;
>>>     +      _Base_ptr        _M_right;
>>>     +
>>>     +      static _Base_ptr
>>>     +      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>     +      {
>>>     +    while (__x->_M_left) __x = __x->_M_left;
>>>     +    return __x;
>>>     +      }
>>>     +
>>>     +      static _Base_ptr
>>>     +      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>     +      {
>>>     +    while (__x->_M_right) __x = __x->_M_right;
>>>     +    return __x;
>>>     +      }
>>>     +    };
>>>     +
>>>     +  struct _Rb_tree_node_base
>>>     +    : _Rb_tree_pnode_base<_Rb_tree_node_base*>
>>>     +  { };
>>     This approaches means that the existing specializations of RB trees
>>     all get twice as many base classes. I don't see an ABI problem, and it
>>     should generate the same code for optimized builds, but increasing the
>>     inheritance depth and the number of class template instantiations
>>     gives the compiler more work to do, compiling slower, and generating
>>     additional debug info (and maybe RTTI?)
>>
>>     Please take a look at how I did it for the linked lists, where
>>     specializations for non-fancy pointers, e.g. _List_node<T>, are mostly
>>     unchanged. They don't get any new base classes, just some new typedefs
>>     and new accessors like:
>>
>>     _Base_ptr _M_base() { return this; }
>>     const _List_node* _M_base() const { return this; }
>>
>>     The _List_node_traits class template is used bystd::list etc. to
>>     select whether to use _List_node<Alloc::value_type> or
>>     __list::_Node<Alloc::pointer>. All the fancy pointer support is in the
>>     new type hierarchy, __list::_Node_base, __list::_Node, and
>>     __list::_Iterator. The old types like _List_node don't really change.
>>
>>     I found that using the __list namespace with new types reduced a lot
>>     of confusion I had between the very similar names _List_node and
>>     _List_pnode (or _List_ptr_node, or other names like that).
>>
>>     Maybe we could call it _List::_Node instead of __list::_Node, so it's
>>     a bit closer to _List_node but still distinguishable (and would
>>     produce very slightly shorter mangled names). I didn't do that,
>>     because our convention is to name impl namespaces as __foo not _Foo.
>>
>>     That pattern could naturally be extended to __tree::_Node (or
>>     _Tree::_Node) as well.
>>
>>>        // Helper type offering value initialization guarantee on the compare functor.
>>>        template<typename _Key_compare>
>>>     @@ -163,81 +219,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>          };
>>>
>>>        // Helper type to manage default initialization of node count and header.
>>>     -  struct _Rb_tree_header
>>>     -  {
>>>     -    _Rb_tree_node_base    _M_header;
>>>     -    size_t        _M_node_count; // Keeps track of size of tree.
>>>     -
>>>     -    _Rb_tree_header() _GLIBCXX_NOEXCEPT
>>>     +  template<typename _NodeBase>
>>>     +    struct _Rb_tree_pheader
>>>          {
>>>     -      _M_header._M_color = _S_red;
>>>     -      _M_reset();
>>>     -    }
>>>     +    private:
>>>     +      typedef typename _NodeBase::_Base_ptr        _Base_ptr;
>>>     +
>>>     +    public:
>>>     +      _NodeBase        _M_header;
>>>     +      size_t        _M_node_count; // Keeps track of size of tree.
>>>     +
>>>     +      _Rb_tree_pheader() _GLIBCXX_NOEXCEPT
>>>     +      {
>>>     +    _M_header._M_color = _S_red;
>>>     +    _M_reset();
>>>     +      }
>>>
>>>     #if __cplusplus >= 201103L
>>>     -    _Rb_tree_header(_Rb_tree_header&& __x) noexcept
>>>     -    {
>>>     -      if (__x._M_header._M_parent != nullptr)
>>>     -    _M_move_data(__x);
>>>     -      else
>>>     -    {
>>>     -      _M_header._M_color = _S_red;
>>>     -      _M_reset();
>>>     -    }
>>>     -    }
>>>     +      _Rb_tree_pheader(_Rb_tree_pheader&& __x) noexcept
>>>     +      {
>>>     +    if (__x._M_header._M_parent)
>>>     +      _M_move_data(__x);
>>>     +    else
>>>     +      {
>>>     +        _M_header._M_color = _S_red;
>>>     +        _M_reset();
>>>     +      }
>>>     +      }
>>>     #endif
>>>
>>>     -    void
>>>     -    _M_move_data(_Rb_tree_header& __from)
>>>     -    {
>>>     -      _M_header._M_color = __from._M_header._M_color;
>>>     -      _M_header._M_parent = __from._M_header._M_parent;
>>>     -      _M_header._M_left = __from._M_header._M_left;
>>>     -      _M_header._M_right = __from._M_header._M_right;
>>>     -      _M_header._M_parent->_M_parent = &_M_header;
>>>     -      _M_node_count = __from._M_node_count;
>>>     -
>>>     -      __from._M_reset();
>>>     -    }
>>>     +      void
>>>     +      _M_move_data(_Rb_tree_pheader& __from)
>>>     +      {
>>>     +    _M_header._M_color = __from._M_header._M_color;
>>>     +    _M_header._M_parent = __from._M_header._M_parent;
>>>     +    _M_header._M_left = __from._M_header._M_left;
>>>     +    _M_header._M_right = __from._M_header._M_right;
>>>     +    _M_header._M_parent->_M_parent = _M_header._M_self_ptr();
>>>     +    _M_node_count = __from._M_node_count;
>>>     +
>>>     +    __from._M_reset();
>>>     +      }
>>>
>>>     -    void
>>>     -    _M_reset()
>>>     -    {
>>>     -      _M_header._M_parent = 0;
>>>     -      _M_header._M_left = &_M_header;
>>>     -      _M_header._M_right = &_M_header;
>>>     -      _M_node_count = 0;
>>>     -    }
>>>     -  };
>>>     +      void
>>>     +      _M_reset()
>>>     +      {
>>>     +    _M_header._M_parent = _Base_ptr();
>>>     +    _M_header._M_left = _M_header._M_right = _M_header._M_self_ptr();
>>>     +    _M_node_count = 0;
>>>     +      }
>>>     +    };
>>>     +
>>>     +  struct _Rb_tree_header : _Rb_tree_pheader<_Rb_tree_node_base>
>>>     +  { };
>>>
>>>        template<typename _Val>
>>>     -    struct _Rb_tree_node : public _Rb_tree_node_base
>>>     +    struct _Rb_tree_node_val
>>>          {
>>>     -      typedef _Rb_tree_node<_Val>* _Link_type;
>>>     -
>>>     #if __cplusplus < 201103L
>>>            _Val _M_value_field;
>>>
>>>     +      __attribute__((__always_inline__))
>>>            _Val*
>>>            _M_valptr()
>>>            { returnstd::__addressof(_M_value_field); }
>>>
>>>     +      __attribute__((__always_inline__))
>>>            const _Val*
>>>            _M_valptr() const
>>>            { returnstd::__addressof(_M_value_field); }
>>>     #else
>>>            __gnu_cxx::__aligned_membuf<_Val> _M_storage;
>>     For the new __list::_Node type I took the opportunity to replace
>>     __aligned_membuf with a union { _Val _M_data; }; which is simpler and
>>     produces the same code. We could probably make that change for the
>>     existing node types, I think it's safe, but making it only for the new
>>     _Node type means the old node type isn't affected.
>>
>>
>>>     +      [[__gnu__::__always_inline__]]
>>>            _Val*
>>>            _M_valptr()
>>>            { return _M_storage._M_ptr(); }
>>>
>>>     +      [[__gnu__::__always_inline__]]
>>>            const _Val*
>>>            _M_valptr() const
>>>            { return _M_storage._M_ptr(); }
>>>     #endif
>>>          };
>>>
>>>     +  template<typename _Val>
>>>     +    struct _Rb_tree_node
>>>     +    : _Rb_tree_node_base
>>>     +    , _Rb_tree_node_val<_Val>
>>>     +    {
>>>     +      typedef _Rb_tree_node<_Val>* _Node_ptr;
>>>     +    };
>>>     +
>>>     +#if __cplusplus >= 201103L
>>>     +  template<typename _ValPtr>
>>>     +    struct _Rb_tree_pnode
>>>     +    : _Rb_tree_pnode_base<_ValPtr>
>>     This makes _Rb_tree_pnode_base depend on the value_type, which means
>>     you instantiate a different specialization of _Rb_tree_pnode_base for
>>     every _Rb_tree. There's no point even having a common base class if
>>     every specialization is different. You might as well just put the
>>     _M_parent/_M_left/_M_right pointers in _Rb_tree_pnode directly.
>>
>>     The point of the node_base class is to have a common base that is not
>>     dependent on the value type, so thatstd::set<int> andstd::set<long>
>>     andstd::set<int, Cmp> all use the same node base. The way this patch
>>     works,std::set<int> will use _Rb_tree_pnode_base<int*> and
>>     std::set<long> will use _Rb_tree_pnode_base<long*>.
>>
>>     This is why my patch for lists uses __ptr_rebind<_ValPtr, void> as the
>>     template argument to _Node_base, so that they all use
>>     _Node_base<void*>.
>>
>>     Obviously for a fancy pointer, it will be a different specialization,
>>     sostd::list<int, A<int>> uses _Node_base<A::void_pointer>, but
>>     std::list<long, A<long>> uses the same _Node_base<A::void_pointer>.
>>
>>>     +    , _Rb_tree_node_val<typenamestd::pointer_traits<_ValPtr>::element_type>
>>>     +    {
>>>     +      using _Node_ptr = __ptr_rebind<_ValPtr, _Rb_tree_pnode>;
>>>     +    };
>>>     +#endif
>>>     +
>>>        _GLIBCXX_PURE _Rb_tree_node_base*
>>>        _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
>>>
>>     [...]
>>
>>>     +#if __cplusplus >= 201103L
>>>     +  template<typename _ValPtr>
>>>     +    struct _Rb_tree_piterator
>>>     +    {
>>>     +      using __ptr_traits = pointer_traits<_ValPtr>;
>>>     +      using value_type = typename __ptr_traits::element_type;
>>>     +      using reference = value_type&;
>>>     +      using pointer = value_type*;
>>>     +
>>>     +      typedef bidirectional_iterator_tag iterator_category;
>>>     +      typedef ptrdiff_t             difference_type;
>>>     +
>>>     +      typedef _Rb_tree_piterator            _Self;
>>>     +      typedef _Rb_tree_pnode_base<_ValPtr>        _Node_base;
>>>     +      typedef _Rb_tree_pnode<_ValPtr>            _Node_type;
>>>     +      typedef typename _Node_type::_Base_ptr        _Base_ptr;
>>>     +      typedef typename _Node_type::_Node_ptr        _Node_ptr;
>>>     +      typedef _Rb_tree_helpers<_Node_base>        _Tree_helpers;
>>>     +
>>>     +      _Rb_tree_piterator() _GLIBCXX_NOEXCEPT
>>>     +      : _M_node() { }
>>>     +
>>>     +      explicit
>>>     +      _Rb_tree_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>     +      : _M_node(__x) { }
>>>     +
>>>     +      reference
>>>     +      operator*() const _GLIBCXX_NOEXCEPT
>>>     +      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>>>     +
>>>     +      pointer
>>>     +      operator->() const _GLIBCXX_NOEXCEPT
>>>     +      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>>>     +
>>>     +      _Self&
>>>     +      operator++() _GLIBCXX_NOEXCEPT
>>>     +      {
>>>     +    _M_node = _Tree_helpers::_Increment(_M_node);
>>>     +    return *this;
>>>     +      }
>>>     +
>>>     +      _Self
>>>     +      operator++(int) _GLIBCXX_NOEXCEPT
>>>     +      {
>>>     +    _Self __tmp = *this;
>>>     +    _M_node = _Tree_helpers::_Increment(_M_node);
>>>     +    return __tmp;
>>>     +      }
>>>     +
>>>     +      _Self&
>>>     +      operator--() _GLIBCXX_NOEXCEPT
>>>     +      {
>>>     +    _M_node = _Tree_helpers::_Decrement(_M_node);
>>>     +    return *this;
>>>     +      }
>>>     +
>>>     +      _Self
>>>     +      operator--(int) _GLIBCXX_NOEXCEPT
>>>     +      {
>>>     +    _Self __tmp = *this;
>>>     +    _M_node = _Tree_helpers::_Decrement(_M_node);
>>>     +    return __tmp;
>>>     +      }
>>>     +
>>>     +      friend bool
>>>     +      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>>>     +      { return __x._M_node == __y._M_node; }
>>>     +
>>>     +#if ! __cpp_lib_three_way_comparison
>>>     +      friend bool
>>>     +      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>>>     +      { return __x._M_node != __y._M_node; }
>>>     +#endif
>>>     +
>>>     +      _Base_ptr _M_node;
>>>     +    };
>>>     +
>>>     +  template<typename _ValPtr>
>>>     +    struct _Rb_tree_const_piterator
>>     For the linked lists, I didn't add two new iterator types, just
>>     _Iterator<_Const, _ValPtr> which is used for iterator and
>>     const_iterator.
>>
>>     It seemed nice not to duplicate the iterator type, since the iterator
>>     and const_iterator are 95% the same. If we don't need to support
>>     C++98, we can write a single iterator implementation.
>>
>>     If you have a single iterator implementation, you probably don't need
>>     the _Rb_tree_helpers.
>>
>>     There would still be uses of insert-and-rebalance and
>>     rebalance-for-erase, but those could just be new overloads for fancy
>>     pointers. I don't think we need the _Rb_tree_helpers class template.
>>
>>
>>>     +    {
>>>     +      using __ptr_traits = pointer_traits<_ValPtr>;
>>>     +      using value_type = typename __ptr_traits::element_type;
>>>     +      using reference = const value_type&;
>>>     +      using pointer = const value_type*;
>>>     +
>>>     +      typedef _Rb_tree_piterator<_ValPtr>        iterator;
>>>     +      typedef typenameiterator::_Base_ptr        _Base_ptr;
>>>     +
>>>     +      typedef bidirectional_iterator_tag iterator_category;
>>>     +      typedef ptrdiff_t             difference_type;
>>>     +
>>>     +      typedef _Rb_tree_const_piterator            _Self;
>>>     +      typedef _Rb_tree_pnode_base<_ValPtr>        _Node_base;
>>>     +      typedef _Rb_tree_pnode<_ValPtr>            _Node_type;
>>>     +      typedef _Rb_tree_helpers<_Node_base>        _Tree_helpers;
>>>     +
>>>     +      _Rb_tree_const_piterator() _GLIBCXX_NOEXCEPT
>>>     +      : _M_node() { }
>>>     +
>>>     +      explicit
>>>     +      _Rb_tree_const_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>     +      : _M_node(__x) { }
>>>     +
>>>     +      _Rb_tree_const_piterator(const iterator& __it) _GLIBCXX_NOEXCEPT
>>>     +      : _M_node(__it._M_node) { }
>>>     +
>>>     +      reference
>>>     +      operator*() const _GLIBCXX_NOEXCEPT
>>>     +      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>>>     +
>>>     +      pointer
>>>     +      operator->() const _GLIBCXX_NOEXCEPT
>>>     +      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>>>     +
>>>     +      _Self&
>>>     +      operator++() _GLIBCXX_NOEXCEPT
>>>     +      {
>>>     +    _M_node = _Tree_helpers::_Increment(_M_node);
>>     You need the iterator increment code here, but you can define it
>>     inline without the _Increment helper.
>>
>>>     +    return *this;
>>>     +      }
>>>     +
>>>     +      _Self
>>>     +      operator++(int) _GLIBCXX_NOEXCEPT
>>>     +      {
>>>     +    _Self __tmp = *this;
>>>     +    _M_node = _Tree_helpers::_Increment(_M_node);
>>     This could just use ++*this. So if there's no separate const iterator
>>     implementation, there's no more use of _Increment.
>>
>>>     +    return __tmp;
>>>     +      }
>>>     +
>>>     +      _Self&
>>>     +      operator--() _GLIBCXX_NOEXCEPT
>>>     +      {
>>>     +    _M_node = _Tree_helpers::_Decrement(_M_node);
>>>     +    return *this;
>>>     +      }
>>>     +
>>>     +      _Self
>>>     +      operator--(int) _GLIBCXX_NOEXCEPT
>>>     +      {
>>>     +    _Self __tmp = *this;
>>>     +    _M_node = _Tree_helpers::_Decrement(_M_node);
>>>     +    return __tmp;
>>>     +      }
>>>     +
>>>     +      friend bool
>>>     +      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>>>     +      { return __x._M_node == __y._M_node; }
>>>     +
>>>     +#if ! __cpp_lib_three_way_comparison
>>>     +      friend bool
>>>     +      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>>>     +      { return __x._M_node != __y._M_node; }
>>>     +#endif
>>>     +
>>>     +      _Base_ptr _M_node;
>>>     +    };
>>>     +#endif // C++11
>>>     +
>>>     +#if __cplusplus >= 201103L
>>>     +  template<typename _ValPtr>
>>>     +    struct _Rb_tree_node_traits
>>>     +    {
>>>     +      using _Node_base        = _Rb_tree_pnode_base<_ValPtr>;
>>>     +      using _Node_type        = _Rb_tree_pnode<_ValPtr>;
>>>     +      using _Header_t        = _Rb_tree_pheader<_Node_base>;
>>>     +      using _Iterator_t        = _Rb_tree_piterator<_ValPtr>;
>>>     +      using _Const_iterator_t    = _Rb_tree_const_piterator<_ValPtr>;
>>>     +    };
>>>     +#else
>>>     +  template<typename _ValPtr>
>>>     +    struct _Rb_tree_node_traits;
>>>     +#endif
>>>     +
>>>     +  template<typename _Val>
>>>     +    struct _Rb_tree_node_traits<_Val*>
>>>     +    {
>>>     +      typedef _Rb_tree_node_base        _Node_base;
>>>     +      typedef _Rb_tree_node<_Val>        _Node_type;
>>>     +      typedef _Rb_tree_header            _Header_t;
>>>     +      typedef _Rb_tree_iterator<_Val>        _Iterator_t;
>>>     +      typedef _Rb_tree_const_iterator<_Val>    _Const_iterator_t;
>>>     +    };
>>>
>>>     #if __cplusplus > 201402L
>>>        template<typename _Tree1, typename _Cmp2>
>>>     @@ -424,16 +1068,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>             typename _Compare, typename _Alloc = allocator<_Val> >
>>>          class _Rb_tree
>>>          {
>>>     +      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::pointer _ValPtr;
>>>     +      typedef _Rb_tree_node_traits<_ValPtr> _Node_traits;
>>>     +
>>>     +      typedef typename _Node_traits::_Node_base        _Node_base;
>>>     +      typedef typename _Node_traits::_Node_type        _Node_type;
>>>     +
>>>            typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
>>>     -    rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
>>>     +    rebind<_Node_type>::other _Node_allocator;
>>>
>>>            typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
>>>
>>>          protected:
>>>     -      typedef _Rb_tree_node_base*         _Base_ptr;
>>>     -      typedef const _Rb_tree_node_base*     _Const_Base_ptr;
>>>     -      typedef _Rb_tree_node<_Val>*         _Link_type;
>>>     -      typedef const _Rb_tree_node<_Val>*    _Const_Link_type;
>>>     +      typedef typename _Node_base::_Base_ptr         _Base_ptr;
>>>     +      typedef typename _Node_type::_Node_ptr         _Node_ptr;
>>     Accessing a member of _Node_type here requires _Node_type to be
>>     complete, which requires _Tp to be complete. That's allowed by the
>>     standard, becausestd::map andstd::set don't support incomplete types
>>     (unlike vector and the linked lists).
>>
>>     But we don't really ned to instantiate _Node_type here, we could use
>>     _Alloc_traits::pointer instead.
>>
>>
>>     [...]
>>
>>>     @@ -1826,10 +2427,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>                        || _M_impl._M_key_compare(_KeyOfValue()(__v),
>>>                                  _S_key(__p)));
>>>
>>>     -    _Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
>>>     +    _Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
>>     I think this relies on implicit conversion from _Node_ptr to
>>     _Base_ptr, which is not necessarily possible. It works for the
>>     _Pointer_adapter type in <ext/pointer.h> because it has a
>>     template<typename U> _Pointer_adapter(_Pointer_adapter<U>) constructor
>>     that supports implicit conversions.
>>
>>     There seems to be a few other places with this problem. Please take a
>>     look at the Pointer type in the new test for my list and forward_list
>>     fancy ptr patches (that Pointer should probably be added to the
>>     testsuite/util/testsuite_allocator.h header for reuse).
>>
>>     I solved these problems by adding a _M_base_ptr() function to the
>>     node_base type, which means that instead of relying on an implicit
>>     conversion from node_ptr to _Base_ptr you call node_ptr->_M_base_ptr()
>>     to get a _Base_ptr.
>>
>>>     @@ -2255,7 +2821,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>          }
>>>            else
>>>          // Equivalent keys.
>>>     -    return _Res(__pos._M_node, 0);
>>>     +    return _Res(__position._M_node, 0);
>>>          }
>>>
>>>        template<typename _Key, typename _Val, typename _KeyOfValue,
>>>     @@ -2294,11 +2860,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>          _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>>>          _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
>>>          {
>>>     -      iterator __pos = __position._M_const_cast();
>>     This removes __pos ...
>>
>>>            typedef pair<_Base_ptr, _Base_ptr> _Res;
>>>
>>>            // end()
>>>     -      if (__pos._M_node == _M_end())
>>>     +      if (__position._M_node == _M_end())
>>>          {
>>>            if (size() > 0
>>>                && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
>>>     @@ -2306,18 +2871,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>            else
>>>              return _M_get_insert_equal_pos(__k);
>>>          }
>>>     -      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
>>>     +      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
>>>          {
>>>            // First, try before...
>>>            iterator __before = __pos;
>>     But it's still used here.
>>
>>>     -      if (__pos._M_node == _M_leftmost()) // begin()
>>>     +      if (__position._M_node == _M_leftmost()) // begin()
>>>              return _Res(_M_leftmost(), _M_leftmost());
>>>            else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
>>>              {
>>>                if (_S_right(__before._M_node) == 0)
>>>              return _Res(0, __before._M_node);
>>>                else
>>>     -        return _Res(__pos._M_node, __pos._M_node);
>>>     +        return _Res(__position._M_node, __position._M_node);
>>>              }
>>>            else
>>>              return _M_get_insert_equal_pos(__k);
>>>     @@ -2326,12 +2891,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>          {
>>>            // ... then try after.
>>>            iterator __after = __pos;
>>     And here.
>>
>>     I think they should both be __position._M_const_cast().
>>
>>
>>>     diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
>>>     new file mode 100644
>>>     index 00000000000..758beedccfd
>>>     --- /dev/null
>>>     +++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
>>>     @@ -0,0 +1,44 @@
>>>     +// { dg-do run { target c++11 } }
>>>     +
>>>     +#include <map>
>>>     +#include <memory>
>>>     +#include <testsuite_hooks.h>
>>>     +#include <testsuite_allocator.h>
>>>     +
>>>     +struct T { int i; };
>>>     +
>>>     +bool operator<(const T& l, const T& r)
>>>     +{ return l.i < r.i; }
>>>     +
>>>     +struct H
>>     This struct H occurs in several tests but seems to be unused.
>>
>>>     +{
>>>     +std::size_t
>>>     +  operator()(const T& t) const noexcept
>>>     +  { return t.i; }
>>>     +};
>>>     +
>>>     +struct L :std::less<T>
>>>     +{ };
>>>     +
>>>     +using __gnu_test::CustomPointerAlloc;
>>>     +
>>>     +template classstd::map<T, int, L,
>>>     +            CustomPointerAlloc<std::pair<const T, int>>>;
>>>     +
>>>     +void test01()
>>>     +{
>>>     +  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
>>>     +  typedefstd::map<T, int, L, alloc_type> test_type;
>>>     +  test_type v;
>>>     +  v.insert({ T(), 0 });
>>>     +  VERIFY( v.begin()->second == 0 );
>>>     +  VERIFY( ++v.begin() == v.end() );
>>>     +  v.insert({ T { 1 }, 1 });
>>>     +  VERIFY( v.size() == 2 );
>>>     +  VERIFY( v.find(T()) != v.end() );
>>>     +}
>>>     +
>>>     +int main()
>>>     +{
>>>     +  test01();
>>>     +}
>>     I'd be interested on your opinion on my patches for fancy pointer
>>     support in the linked lists. I think it's cleaner and requires fewer
>>     changes to the existing specializations.
>>
>>     My changes can also be disabled by a macro (to avoid breaking any
>>     users who are somehow managing to use allocators-with-fancy-pointers
>>     with RB trees today, which could be useful unless that's completely
>>     broken today), and to be enabled for non-fancy pointers (for test
>>     coverage).
>>
diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
index bc27e191e8b..bed7266c688 100644
--- a/libstdc++-v3/include/bits/stl_tree.h
+++ b/libstdc++-v3/include/bits/stl_tree.h
@@ -66,6 +66,7 @@
 #include <bits/allocator.h>
 #include <bits/stl_function.h>
 #include <bits/cpp_type_traits.h>
+#include <bits/ptr_traits.h>
 #include <ext/alloc_traits.h>
 #if __cplusplus >= 201103L
 # include <ext/aligned_buffer.h>
@@ -74,6 +75,13 @@
 # include <bits/node_handle.h>
 #endif
 
+#if __cplusplus < 201103L
+# undef _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+# define _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE 0
+#elif ! defined _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+# define _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE 1
+#endif
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -99,7 +107,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   struct _Rb_tree_node_base
   {
     typedef _Rb_tree_node_base* _Base_ptr;
-    typedef const _Rb_tree_node_base* _Const_Base_ptr;
 
     _Rb_tree_color	_M_color;
     _Base_ptr		_M_parent;
@@ -113,13 +120,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return __x;
     }
 
-    static _Const_Base_ptr
-    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-    {
-      while (__x->_M_left != 0) __x = __x->_M_left;
-      return __x;
-    }
-
     static _Base_ptr
     _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
     {
@@ -127,12 +127,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return __x;
     }
 
-    static _Const_Base_ptr
-    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-    {
-      while (__x->_M_right != 0) __x = __x->_M_right;
-      return __x;
-    }
+    // This is not const-correct, but it's only used in a const access path
+    // by std::_Rb_tree::_M_end() where the pointer is used to initialize a
+    // const_iterator and so constness is restored.
+    _Base_ptr
+    _M_base_ptr() const _GLIBCXX_NOEXCEPT
+    { return const_cast<_Rb_tree_node_base*>(this); }
+
+    template<typename _NodePtr>
+      _NodePtr
+      _M_node_ptr() _GLIBCXX_NOEXCEPT
+      { return static_cast<_NodePtr>(this); }
   };
 
   // Helper type offering value initialization guarantee on the compare functor.
@@ -213,7 +218,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Val>
     struct _Rb_tree_node : public _Rb_tree_node_base
     {
-      typedef _Rb_tree_node<_Val>* _Link_type;
+      typedef _Rb_tree_node<_Val>* _Node_ptr;
 
 #if __cplusplus < 201103L
       _Val _M_value_field;
@@ -238,18 +243,136 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
     };
 
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+namespace __rb_tree
+{
+  template<typename _VoidPtr>
+    struct _Node_base
+    {
+      using _Base_ptr = __ptr_rebind<_VoidPtr, _Node_base>;
+
+      _Rb_tree_color	_M_color;
+      _Base_ptr		_M_parent;
+      _Base_ptr		_M_left;
+      _Base_ptr		_M_right;
+
+      static _Base_ptr
+      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      {
+	while (__x->_M_left) __x = __x->_M_left;
+	return __x;
+      }
+
+      static _Base_ptr
+      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      {
+	while (__x->_M_right) __x = __x->_M_right;
+	return __x;
+      }
+
+      // This is not const-correct, but it's only used in a const access path
+      // by std::_Rb_tree::_M_end() where the pointer is used to initialize a
+      // const_iterator and so constness is restored.
+      _Base_ptr
+      _M_base_ptr() const
+      {
+	return pointer_traits<_Base_ptr>::pointer_to
+	  (*const_cast<_Node_base*>(this));
+      }
+
+      template<typename _NodePtr>
+	_NodePtr
+	_M_node_ptr() _GLIBCXX_NOEXCEPT
+	{
+	  using __ptr_traits = pointer_traits<_NodePtr>;
+	  using __node_type = typename __ptr_traits::element_type;
+	  auto __this = static_cast<__node_type*>(this);
+	  return __ptr_traits::pointer_to(*__this);
+	}
+    };
+
+  // Helper type to manage default initialization of node count and header.
+  template<typename _NodeBase>
+    struct _Header
+    {
+    private:
+      using _Base_ptr =  typename _NodeBase::_Base_ptr;
+
+    public:
+      _NodeBase		_M_header;
+      size_t		_M_node_count; // Keeps track of size of tree.
+
+      _Header() noexcept
+      {
+	_M_header._M_color = _S_red;
+	_M_reset();
+      }
+
+      _Header(_Header&& __x) noexcept
+      {
+	if (__x._M_header._M_parent)
+	  _M_move_data(__x);
+	else
+	  {
+	    _M_header._M_color = _S_red;
+	    _M_reset();
+	  }
+      }
+
+      void
+      _M_move_data(_Header& __from)
+      {
+	_M_header._M_color = __from._M_header._M_color;
+	_M_header._M_parent = __from._M_header._M_parent;
+	_M_header._M_left = __from._M_header._M_left;
+	_M_header._M_right = __from._M_header._M_right;
+	_M_header._M_parent->_M_parent = _M_header._M_base_ptr();
+	_M_node_count = __from._M_node_count;
+
+	__from._M_reset();
+      }
+
+      void
+      _M_reset()
+      {
+	_M_header._M_parent = _Base_ptr{};
+	_M_header._M_left = _M_header._M_right = _M_header._M_base_ptr();
+	_M_node_count = 0;
+      }
+    };
+
+  template<typename _ValPtr>
+    struct _Node : public __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>
+    {
+      using value_type = typename pointer_traits<_ValPtr>::element_type;
+      using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
+
+      union
+      {
+	value_type _M_storage;
+      };
+
+      _Node() { }
+      ~_Node() { }
+      _Node(_Node&&) = delete;
+
+      value_type*
+      _M_valptr()
+      { return std::__addressof(_M_storage); }
+
+      value_type const*
+      _M_valptr() const
+      { return std::__addressof(_M_storage); }
+    };
+} // namespace __rb_tree
+#endif
+
   _GLIBCXX_PURE _Rb_tree_node_base*
   _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
 
-  _GLIBCXX_PURE const _Rb_tree_node_base*
-  _Rb_tree_increment(const _Rb_tree_node_base* __x) throw ();
-
   _GLIBCXX_PURE _Rb_tree_node_base*
   _Rb_tree_decrement(_Rb_tree_node_base* __x) throw ();
 
-  _GLIBCXX_PURE const _Rb_tree_node_base*
-  _Rb_tree_decrement(const _Rb_tree_node_base* __x) throw ();
-
   template<typename _Tp>
     struct _Rb_tree_iterator
     {
@@ -260,9 +383,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef bidirectional_iterator_tag iterator_category;
       typedef ptrdiff_t			 difference_type;
 
-      typedef _Rb_tree_iterator<_Tp>		_Self;
       typedef _Rb_tree_node_base::_Base_ptr	_Base_ptr;
-      typedef _Rb_tree_node<_Tp>*		_Link_type;
+      typedef _Rb_tree_node<_Tp>*		_Node_ptr;
 
       _Rb_tree_iterator() _GLIBCXX_NOEXCEPT
       : _M_node() { }
@@ -273,49 +395,51 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       reference
       operator*() const _GLIBCXX_NOEXCEPT
-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type> (_M_node)->_M_valptr(); }
+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
-      _Self&
+      _Rb_tree_iterator&
       operator++() _GLIBCXX_NOEXCEPT
       {
 	_M_node = _Rb_tree_increment(_M_node);
 	return *this;
       }
 
-      _Self
+      _Rb_tree_iterator
       operator++(int) _GLIBCXX_NOEXCEPT
       {
-	_Self __tmp = *this;
+	_Rb_tree_iterator __tmp = *this;
 	_M_node = _Rb_tree_increment(_M_node);
 	return __tmp;
       }
 
-      _Self&
+      _Rb_tree_iterator&
       operator--() _GLIBCXX_NOEXCEPT
       {
 	_M_node = _Rb_tree_decrement(_M_node);
 	return *this;
       }
 
-      _Self
+      _Rb_tree_iterator
       operator--(int) _GLIBCXX_NOEXCEPT
       {
-	_Self __tmp = *this;
+	_Rb_tree_iterator __tmp = *this;
 	_M_node = _Rb_tree_decrement(_M_node);
 	return __tmp;
       }
 
       friend bool
-      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      operator==(const _Rb_tree_iterator& __x,
+		 const _Rb_tree_iterator& __y) _GLIBCXX_NOEXCEPT
       { return __x._M_node == __y._M_node; }
 
 #if ! __cpp_lib_three_way_comparison
       friend bool
-      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      operator!=(const _Rb_tree_iterator& __x,
+		 const _Rb_tree_iterator& __y) _GLIBCXX_NOEXCEPT
       { return __x._M_node != __y._M_node; }
 #endif
 
@@ -334,9 +458,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef bidirectional_iterator_tag iterator_category;
       typedef ptrdiff_t			 difference_type;
 
-      typedef _Rb_tree_const_iterator<_Tp>		_Self;
-      typedef _Rb_tree_node_base::_Const_Base_ptr	_Base_ptr;
-      typedef const _Rb_tree_node<_Tp>*			_Link_type;
+      typedef _Rb_tree_node_base::_Base_ptr	_Base_ptr;
+      typedef const _Rb_tree_node<_Tp>*		_Node_ptr;
 
       _Rb_tree_const_iterator() _GLIBCXX_NOEXCEPT
       : _M_node() { }
@@ -348,55 +471,53 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree_const_iterator(const iterator& __it) _GLIBCXX_NOEXCEPT
       : _M_node(__it._M_node) { }
 
-      iterator
-      _M_const_cast() const _GLIBCXX_NOEXCEPT
-      { return iterator(const_cast<typename iterator::_Base_ptr>(_M_node)); }
-
       reference
       operator*() const _GLIBCXX_NOEXCEPT
-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
-      _Self&
+      _Rb_tree_const_iterator&
       operator++() _GLIBCXX_NOEXCEPT
       {
 	_M_node = _Rb_tree_increment(_M_node);
 	return *this;
       }
 
-      _Self
+      _Rb_tree_const_iterator
       operator++(int) _GLIBCXX_NOEXCEPT
       {
-	_Self __tmp = *this;
+	_Rb_tree_const_iterator __tmp = *this;
 	_M_node = _Rb_tree_increment(_M_node);
 	return __tmp;
       }
 
-      _Self&
+      _Rb_tree_const_iterator&
       operator--() _GLIBCXX_NOEXCEPT
       {
 	_M_node = _Rb_tree_decrement(_M_node);
 	return *this;
       }
 
-      _Self
+      _Rb_tree_const_iterator
       operator--(int) _GLIBCXX_NOEXCEPT
       {
-	_Self __tmp = *this;
+	_Rb_tree_const_iterator __tmp = *this;
 	_M_node = _Rb_tree_decrement(_M_node);
 	return __tmp;
       }
 
       friend bool
-      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      operator==(const _Rb_tree_const_iterator& __x,
+		 const _Rb_tree_const_iterator& __y) _GLIBCXX_NOEXCEPT
       { return __x._M_node == __y._M_node; }
 
 #if ! __cpp_lib_three_way_comparison
       friend bool
-      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      operator!=(const _Rb_tree_const_iterator& __x,
+		 const _Rb_tree_const_iterator& __y) _GLIBCXX_NOEXCEPT
       { return __x._M_node != __y._M_node; }
 #endif
 
@@ -415,6 +536,485 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
 			       _Rb_tree_node_base& __header) throw ();
 
+namespace __rb_tree
+{
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+  template<bool _Const, typename _ValPtr>
+    struct _Iterator
+    {
+      template<typename _Tp>
+	using __maybe_const = __conditional_t<_Const, const _Tp, _Tp>;
+
+      using __ptr_traits =	pointer_traits<_ValPtr>;
+      using value_type =	typename __ptr_traits::element_type;
+      using reference =		__maybe_const<value_type>&;
+      using pointer =		__maybe_const<value_type>*;
+
+      using iterator_category =	bidirectional_iterator_tag;
+      using difference_type =	ptrdiff_t;
+
+      using _Node = __rb_tree::_Node<_ValPtr>;
+      using _Node_base = __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>;
+      using _Base_ptr =	 typename _Node_base::_Base_ptr;
+
+      _Iterator() _GLIBCXX_NOEXCEPT
+      : _M_node() { }
+
+      constexpr explicit
+      _Iterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      : _M_node(__x) { }
+
+#if __cplusplus >= 201103L
+      _Iterator(const _Iterator&) = default;
+      _Iterator& operator=(const _Iterator&) = default;
+#endif
+
+#ifdef __glibcxx_concepts
+      constexpr
+      _Iterator(const _Iterator<false, _ValPtr>& __it) requires _Const
+#else
+      template<bool _OtherConst,
+	       typename = __enable_if_t<_Const && !_OtherConst>>
+	constexpr
+	_Iterator(const _Iterator<_OtherConst, _ValPtr>& __it)
+#endif
+	: _M_node(__it._M_node) { }
+
+      [[__nodiscard__]]
+      reference
+      operator*() const _GLIBCXX_NOEXCEPT
+      { return *static_cast<_Node&>(*_M_node)._M_valptr(); }
+
+      [[__nodiscard__]]
+      pointer
+      operator->() const _GLIBCXX_NOEXCEPT
+      { return static_cast<_Node&>(*_M_node)._M_valptr(); }
+
+      _GLIBCXX14_CONSTEXPR _Iterator&
+      operator++() _GLIBCXX_NOEXCEPT
+      {
+	if (_M_node->_M_right)
+	  {
+	    _M_node = _M_node->_M_right;
+	    while (_M_node->_M_left)
+	      _M_node = _M_node->_M_left;
+	  }
+	else
+	  {
+	    _Base_ptr __y = _M_node->_M_parent;
+	    while (_M_node == __y->_M_right)
+	      {
+		_M_node = __y;
+		__y = __y->_M_parent;
+	      }
+	    if (_M_node->_M_right != __y)
+	      _M_node = __y;
+	  }
+
+	return *this;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator
+      operator++(int) _GLIBCXX_NOEXCEPT
+      {
+	_Iterator __tmp(this->_M_node);
+	++*this;
+	return __tmp;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator&
+      operator--() _GLIBCXX_NOEXCEPT
+      {
+	if (_M_node->_M_color == _S_red
+	    && _M_node->_M_parent->_M_parent == _M_node)
+	  _M_node = _M_node->_M_right;
+	else if (_M_node->_M_left)
+	  {
+	    _Base_ptr __y = _M_node->_M_left;
+	    while (__y->_M_right)
+	      __y = __y->_M_right;
+	    _M_node = __y;
+	  }
+	else
+	  {
+	    _Base_ptr __y = _M_node->_M_parent;
+	    while (_M_node == __y->_M_left)
+	      {
+		_M_node = __y;
+		__y = __y->_M_parent;
+	      }
+	    _M_node = __y;
+	  }
+	return *this;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator
+      operator--(int) _GLIBCXX_NOEXCEPT
+      {
+	_Iterator __tmp(this->_M_node);
+	--*this;
+	return __tmp;
+      }
+
+      [[__nodiscard__]]
+      friend bool
+      operator==(const _Iterator& __x, const _Iterator& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node == __y._M_node; }
+
+#if ! __cpp_lib_three_way_comparison
+      [[__nodiscard__]]
+      friend bool
+      operator!=(const _Iterator& __x, const _Iterator& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node != __y._M_node; }
+#endif
+
+      _Base_ptr _M_node;
+    };
+#endif // USE_ALLOC_PTR_FOR_RB_TREE
+
+  // Determine the node and iterator types used by std::_Rb_tree.
+  template<typename _Val, typename _Ptr>
+    struct _Node_traits;
+
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE <= 9000
+  // Specialization for the simple case where the allocator's pointer type
+  // is the same type as value_type*.
+  // For ABI compatibility we can't change the types used for this case.
+  template<typename _Val>
+    struct _Node_traits<_Val, _Val*>
+    {
+      typedef _Rb_tree_node<_Val>		_Node;
+      typedef _Rb_tree_node_base		_Node_base;
+      typedef _Rb_tree_header			_Header_t;
+      typedef _Rb_tree_iterator<_Val>		_Iterator;
+      typedef _Rb_tree_const_iterator<_Val>	_Const_iterator;
+
+      __attribute__((__nonnull__))
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _Node_base* __x, _Node_base* __p,
+			      _Node_base& __header) _GLIBCXX_USE_NOEXCEPT
+      {
+	return _Rb_tree_insert_and_rebalance(__insert_left, __x, __p, __header);
+      }
+
+      __attribute__((__nonnull__,__returns_nonnull__))
+      static _Node_base*
+      _S_rebalance_for_erase(_Node_base* const __z,
+			     _Node_base& __header) _GLIBCXX_USE_NOEXCEPT
+      { return _Rb_tree_rebalance_for_erase(__z, __header); }
+    };
+#endif
+
+#if ! _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+  // Always use the T* specialization.
+  template<typename _Val, typename _Ptr>
+    struct _Node_traits
+    : _Node_traits<_Val, _Val*>
+    { };
+#else
+  // Primary template used when the allocator uses fancy pointers.
+  template<typename _Val, typename _ValPtr>
+    struct _Node_traits
+    {
+      using _Node = __rb_tree::_Node<_ValPtr>;
+      using _Node_base = __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>;
+      using _Header_t = __rb_tree::_Header<_Node_base>;
+      using _Iterator = __rb_tree::_Iterator<false, _ValPtr>;
+      using _Const_iterator = __rb_tree::_Iterator<true, _ValPtr>;
+
+      using _Base_ptr = typename _Node_base::_Base_ptr;
+
+      static void
+      _Rotate_left(_Base_ptr __x, _Base_ptr& __root)
+      {
+	const _Base_ptr __y = __x->_M_right;
+
+	__x->_M_right = __y->_M_left;
+	if (__y->_M_left)
+	  __y->_M_left->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_left)
+	  __x->_M_parent->_M_left = __y;
+	else
+	  __x->_M_parent->_M_right = __y;
+	__y->_M_left = __x;
+	__x->_M_parent = __y;
+      }
+
+      static void
+      _Rotate_right(_Base_ptr __x, _Base_ptr& __root)
+      {
+	const _Base_ptr __y = __x->_M_left;
+
+	__x->_M_left = __y->_M_right;
+	if (__y->_M_right)
+	  __y->_M_right->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_right)
+	  __x->_M_parent->_M_right = __y;
+	else
+	  __x->_M_parent->_M_left = __y;
+	__y->_M_right = __x;
+	__x->_M_parent = __y;
+      }
+
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _Base_ptr __x, _Base_ptr __p,
+			      _Node_base& __header)
+      {
+	_Base_ptr& __root = __header._M_parent;
+
+	// Initialize fields in new node to insert.
+	__x->_M_parent = __p;
+	__x->_M_left = __x->_M_right = nullptr;
+	__x->_M_color = _S_red;
+
+	// Insert.
+	// Make new node child of parent and maintain root, leftmost and
+	// rightmost nodes.
+	// N.B. First node is always inserted left.
+	if (__insert_left)
+	  {
+	    __p->_M_left = __x; // also makes leftmost = __x when __p == &__header
+
+	    if (std::__to_address(__p) == std::addressof(__header))
+	      {
+		__header._M_parent = __x;
+		__header._M_right = __x;
+	      }
+	    else if (__p == __header._M_left)
+	      __header._M_left = __x; // maintain leftmost pointing to min node
+	  }
+	else
+	  {
+	    __p->_M_right = __x;
+
+	    if (__p == __header._M_right)
+	      __header._M_right = __x; // maintain rightmost pointing to max node
+	  }
+	// Rebalance.
+	while (__x != __root
+	       && __x->_M_parent->_M_color == _S_red)
+	  {
+	    const _Base_ptr __xpp = __x->_M_parent->_M_parent;
+
+	    if (__x->_M_parent == __xpp->_M_left)
+	      {
+		const _Base_ptr __y = __xpp->_M_right;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_right)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_left(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_right(__xpp, __root);
+		  }
+	      }
+	    else
+	      {
+		const _Base_ptr __y = __xpp->_M_left;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_left)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_right(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_left(__xpp, __root);
+		  }
+	      }
+	  }
+	__root->_M_color = _S_black;
+      }
+
+      static _Base_ptr
+      _S_rebalance_for_erase(_Base_ptr __z, _Node_base& __header)
+      {
+	_Base_ptr& __root = __header._M_parent;
+	_Base_ptr& __leftmost = __header._M_left;
+	_Base_ptr& __rightmost = __header._M_right;
+	_Base_ptr __y = __z;
+	_Base_ptr __x{};
+	_Base_ptr __x_parent{};
+
+	if (!__y->_M_left)     // __z has at most one non-null child. y == z.
+	  __x = __y->_M_right;     // __x might be null.
+	else
+	  if (!__y->_M_right)  // __z has exactly one non-null child. y == z.
+	    __x = __y->_M_left;    // __x is not null.
+	  else
+	    {
+	      // __z has two non-null children.  Set __y to
+	      __y = __y->_M_right;   //   __z's successor.  __x might be null.
+	      while (__y->_M_left)
+		__y = __y->_M_left;
+	      __x = __y->_M_right;
+	    }
+	if (__y != __z)
+	  {
+	    // relink y in place of z.  y is z's successor
+	    __z->_M_left->_M_parent = __y;
+	    __y->_M_left = __z->_M_left;
+	    if (__y != __z->_M_right)
+	      {
+		__x_parent = __y->_M_parent;
+		if (__x)
+		  __x->_M_parent = __y->_M_parent;
+		__y->_M_parent->_M_left = __x;   // __y must be a child of _M_left
+		__y->_M_right = __z->_M_right;
+		__z->_M_right->_M_parent = __y;
+	      }
+	    else
+	      __x_parent = __y;
+	    if (__root == __z)
+	      __root = __y;
+	    else if (__z->_M_parent->_M_left == __z)
+	      __z->_M_parent->_M_left = __y;
+	    else
+	      __z->_M_parent->_M_right = __y;
+	    __y->_M_parent = __z->_M_parent;
+	    std::swap(__y->_M_color, __z->_M_color);
+	    __y = __z;
+	    // __y now points to node to be actually deleted
+	  }
+	else
+	  {                        // __y == __z
+	    __x_parent = __y->_M_parent;
+	    if (__x)
+	      __x->_M_parent = __y->_M_parent;
+	    if (__root == __z)
+	      __root = __x;
+	    else
+	      if (__z->_M_parent->_M_left == __z)
+		__z->_M_parent->_M_left = __x;
+	      else
+		__z->_M_parent->_M_right = __x;
+	    if (__leftmost == __z)
+	      {
+		if (!__z->_M_right)        // __z->_M_left must be null also
+		  __leftmost = __z->_M_parent;
+		// makes __leftmost == _M_header if __z == __root
+		else
+		  __leftmost = _Node_base::_S_minimum(__x);
+	      }
+	    if (__rightmost == __z)
+	      {
+		if (__z->_M_left == 0)         // __z->_M_right must be null also
+		  __rightmost = __z->_M_parent;
+		// makes __rightmost == _M_header if __z == __root
+		else                      // __x == __z->_M_left
+		  __rightmost = _Node_base::_S_maximum(__x);
+	      }
+	  }
+	if (__y->_M_color != _S_red)
+	  {
+	    while (__x != __root && (__x == 0 || __x->_M_color == _S_black))
+	      if (__x == __x_parent->_M_left)
+		{
+		  _Base_ptr __w = __x_parent->_M_right;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_left(__x_parent, __root);
+		      __w = __x_parent->_M_right;
+		    }
+		  if ((!__w->_M_left || __w->_M_left->_M_color == _S_black) &&
+		      (!__w->_M_right || __w->_M_right->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_right || __w->_M_right->_M_color == _S_black)
+			{
+			  __w->_M_left->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_right(__w, __root);
+			  __w = __x_parent->_M_right;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_right)
+			__w->_M_right->_M_color = _S_black;
+		      _Rotate_left(__x_parent, __root);
+		      break;
+		    }
+		}
+	      else
+		{
+		  // same as above, with _M_right <-> _M_left.
+		  _Base_ptr __w = __x_parent->_M_left;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_right(__x_parent, __root);
+		      __w = __x_parent->_M_left;
+		    }
+		  if ((!__w->_M_right || __w->_M_right->_M_color == _S_black) &&
+		      (!__w->_M_left || __w->_M_left->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_left || __w->_M_left->_M_color == _S_black)
+			{
+			  __w->_M_right->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_left(__w, __root);
+			  __w = __x_parent->_M_left;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_left)
+			__w->_M_left->_M_color = _S_black;
+		      _Rotate_right(__x_parent, __root);
+		      break;
+		    }
+		}
+	    if (__x)
+	      __x->_M_color = _S_black;
+	  }
+
+	return __y;
+      }
+    };
+#endif
+} // namespace __rb_tree
+
 #if __cplusplus > 201402L
   template<typename _Tree1, typename _Cmp2>
     struct _Rb_tree_merge_helper { };
@@ -425,15 +1025,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     class _Rb_tree
     {
       typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
-	rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
+	rebind<_Val>::other _Val_alloc_type;
+
+      typedef __gnu_cxx::__alloc_traits<_Val_alloc_type> _Val_alloc_traits;
+      typedef typename _Val_alloc_traits::pointer _ValPtr;
+      typedef __rb_tree::_Node_traits<_Val, _ValPtr> _Node_traits;
+
+      typedef typename _Node_traits::_Node_base		_Node_base;
+      typedef typename _Node_traits::_Node		_Node;
+
+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
+	rebind<_Node>::other _Node_allocator;
 
-      typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
+      typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Node_alloc_traits;
 
     protected:
-      typedef _Rb_tree_node_base* 		_Base_ptr;
-      typedef const _Rb_tree_node_base* 	_Const_Base_ptr;
-      typedef _Rb_tree_node<_Val>* 		_Link_type;
-      typedef const _Rb_tree_node<_Val>*	_Const_Link_type;
+      typedef typename _Node_base::_Base_ptr 		_Base_ptr;
+      typedef typename _Node_alloc_traits::pointer	_Node_ptr;
 
     private:
       // Functor recycling a pool of nodes and using allocation once the pool
@@ -445,13 +1053,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  if (_M_root)
 	    {
-	      _M_root->_M_parent = 0;
+	      _M_root->_M_parent = _Base_ptr();
 
 	      if (_M_nodes->_M_left)
 		_M_nodes = _M_nodes->_M_left;
 	    }
 	  else
-	    _M_nodes = 0;
+	    _M_nodes = _Base_ptr();
 	}
 
 #if __cplusplus >= 201103L
@@ -459,13 +1067,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
 	~_Reuse_or_alloc_node()
-	{ _M_t._M_erase(static_cast<_Link_type>(_M_root)); }
+	{ _M_t._M_erase(_M_root); }
 
 	template<typename _Arg>
-	  _Link_type
+	  _Base_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg)
 	  {
-	    _Link_type __node = static_cast<_Link_type>(_M_extract());
+	    _Base_ptr __node = _M_extract();
 	    if (__node)
 	      {
 		_M_t._M_destroy_node(__node);
@@ -489,7 +1097,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    {
 	      if (_M_nodes->_M_right == __node)
 		{
-		  _M_nodes->_M_right = 0;
+		  _M_nodes->_M_right = _Base_ptr();
 
 		  if (_M_nodes->_M_left)
 		    {
@@ -503,10 +1111,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		    }
 		}
 	      else // __node is on the left.
-		_M_nodes->_M_left = 0;
+		_M_nodes->_M_left = _Base_ptr();
 	    }
 	  else
-	    _M_root = 0;
+	    _M_root = _Base_ptr();
 
 	  return __node;
 	}
@@ -524,9 +1132,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: _M_t(__t) { }
 
 	template<typename _Arg>
-	  _Link_type
+	  _Base_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg) const
-	  { return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg)); }
+	  {
+	    return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg));
+	  }
 
       private:
 	_Rb_tree& _M_t;
@@ -556,18 +1166,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return allocator_type(_M_get_Node_allocator()); }
 
     protected:
-      _Link_type
+      _Node_ptr
       _M_get_node()
-      { return _Alloc_traits::allocate(_M_get_Node_allocator(), 1); }
+      { return _Node_alloc_traits::allocate(_M_get_Node_allocator(), 1); }
 
       void
-      _M_put_node(_Link_type __p) _GLIBCXX_NOEXCEPT
-      { _Alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1); }
+      _M_put_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
+      {
+	_Node_alloc_traits::deallocate
+	  (_M_get_Node_allocator(), __p->template _M_node_ptr<_Node_ptr>(), 1);
+      }
 
 #if __cplusplus < 201103L
       void
-      _M_construct_node(_Link_type __node, const value_type& __x)
+      _M_construct_node(_Base_ptr __p, const value_type& __x)
       {
+	_Node_ptr __node = static_cast<_Node_ptr>(__p);
 	__try
 	  { get_allocator().construct(__node->_M_valptr(), __x); }
 	__catch(...)
@@ -577,79 +1191,83 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  }
       }
 
-      _Link_type
+      _Base_ptr
       _M_create_node(const value_type& __x)
       {
-	_Link_type __tmp = _M_get_node();
+	_Base_ptr __tmp = _M_get_node()->_M_base_ptr();
 	_M_construct_node(__tmp, __x);
 	return __tmp;
       }
 #else
       template<typename... _Args>
 	void
-	_M_construct_node(_Link_type __node, _Args&&... __args)
+	_M_construct_node(_Base_ptr __p, _Args&&... __args)
 	{
+	  _Node& __node = static_cast<_Node&>(*__p);
 	  __try
 	    {
-	      ::new(__node) _Rb_tree_node<_Val>;
-	      _Alloc_traits::construct(_M_get_Node_allocator(),
-				       __node->_M_valptr(),
-				       std::forward<_Args>(__args)...);
+	      ::new(std::__addressof(__node)) _Node;
+	      _Node_alloc_traits::construct(_M_get_Node_allocator(),
+					    __node._M_valptr(),
+					    std::forward<_Args>(__args)...);
 	    }
 	  __catch(...)
 	    {
-	      __node->~_Rb_tree_node<_Val>();
-	      _M_put_node(__node);
+	      __node.~_Node();
+	      _M_put_node(__p);
 	      __throw_exception_again;
 	    }
 	}
 
       template<typename... _Args>
-	_Link_type
+	_Base_ptr
 	_M_create_node(_Args&&... __args)
 	{
-	  _Link_type __tmp = _M_get_node();
+	  _Base_ptr __tmp = _M_get_node()->_M_base_ptr();
 	  _M_construct_node(__tmp, std::forward<_Args>(__args)...);
 	  return __tmp;
 	}
 #endif
 
       void
-      _M_destroy_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_destroy_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
       {
 #if __cplusplus < 201103L
-	get_allocator().destroy(__p->_M_valptr());
+	get_allocator().destroy(static_cast<_Node_ptr>(__p)->_M_valptr());
 #else
-	_Alloc_traits::destroy(_M_get_Node_allocator(), __p->_M_valptr());
-	__p->~_Rb_tree_node<_Val>();
+	_Node& __node = static_cast<_Node&>(*__p);
+	_Node_alloc_traits::destroy(_M_get_Node_allocator(),
+				    __node._M_valptr());
+	__node.~_Node();
 #endif
       }
 
       void
-      _M_drop_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_drop_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
       {
 	_M_destroy_node(__p);
 	_M_put_node(__p);
       }
 
       template<bool _MoveValue, typename _NodeGen>
-	_Link_type
-	_M_clone_node(_Link_type __x, _NodeGen& __node_gen)
+	_Base_ptr
+	_M_clone_node(_Base_ptr __x, _NodeGen& __node_gen)
 	{
 #if __cplusplus >= 201103L
 	  using _Vp = __conditional_t<_MoveValue,
 				      value_type&&,
 				      const value_type&>;
 #endif
-	  _Link_type __tmp
-	    = __node_gen(_GLIBCXX_FORWARD(_Vp, *__x->_M_valptr()));
+	  _Base_ptr __tmp = __node_gen
+	    (_GLIBCXX_FORWARD(_Vp, *static_cast<_Node&>(*__x)._M_valptr()));
 	  __tmp->_M_color = __x->_M_color;
-	  __tmp->_M_left = 0;
-	  __tmp->_M_right = 0;
+	  __tmp->_M_left = __tmp->_M_right = _Base_ptr();
 	  return __tmp;
 	}
 
     protected:
+      typedef typename _Node_traits::_Header_t		_Header_t;
+
 #if _GLIBCXX_INLINE_VERSION
       template<typename _Key_compare>
 #else
@@ -660,7 +1278,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	struct _Rb_tree_impl
 	: public _Node_allocator
 	, public _Rb_tree_key_compare<_Key_compare>
-	, public _Rb_tree_header
+	, public _Header_t
 	{
 	  typedef _Rb_tree_key_compare<_Key_compare> _Base_key_compare;
 
@@ -672,9 +1290,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  { }
 
 	  _Rb_tree_impl(const _Rb_tree_impl& __x)
-	  : _Node_allocator(_Alloc_traits::_S_select_on_copy(__x))
+	  : _Node_allocator(_Node_alloc_traits::_S_select_on_copy(__x))
 	  , _Base_key_compare(__x._M_key_compare)
-	  , _Rb_tree_header()
+	  , _Header_t()
 	  { }
 
 #if __cplusplus < 201103L
@@ -694,7 +1312,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Rb_tree_impl(_Rb_tree_impl&& __x, _Node_allocator&& __a)
 	  : _Node_allocator(std::move(__a)),
 	    _Base_key_compare(std::move(__x)),
-	    _Rb_tree_header(std::move(__x))
+	    _Header_t(std::move(__x))
 	  { }
 
 	  _Rb_tree_impl(const _Key_compare& __comp, _Node_allocator&& __a)
@@ -710,7 +1328,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_root() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_parent; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_root() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_parent; }
 
@@ -718,7 +1336,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_leftmost() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_left; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_leftmost() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_left; }
 
@@ -726,35 +1344,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_rightmost() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_right; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_rightmost() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_right; }
 
-      _Link_type
-      _M_mbegin() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(this->_M_impl._M_header._M_parent); }
-
-      _Link_type
-      _M_begin() _GLIBCXX_NOEXCEPT
-      { return _M_mbegin(); }
-
-      _Const_Link_type
+      _Base_ptr
       _M_begin() const _GLIBCXX_NOEXCEPT
-      {
-	return static_cast<_Const_Link_type>
-	  (this->_M_impl._M_header._M_parent);
-      }
+      { return this->_M_impl._M_header._M_parent; }
 
       _Base_ptr
-      _M_end() _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
-
-      _Const_Base_ptr
       _M_end() const _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
+      { return this->_M_impl._M_header._M_base_ptr(); }
 
       static const _Key&
-      _S_key(_Const_Link_type __x)
+      _S_key(_Base_ptr __x)
       {
 #if __cplusplus >= 201103L
 	// If we're asking for the key we're presumably using the comparison
@@ -772,48 +1375,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 # endif // C++17
 #endif // C++11
 
-	return _KeyOfValue()(*__x->_M_valptr());
+	return _KeyOfValue()(*static_cast<const _Node&>(*__x)._M_valptr());
       }
 
-      static _Link_type
-      _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_left); }
-
-      static _Const_Link_type
-      _S_left(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_left); }
-
-      static _Link_type
-      _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_right); }
-
-      static _Const_Link_type
-      _S_right(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_right); }
-
-      static const _Key&
-      _S_key(_Const_Base_ptr __x)
-      { return _S_key(static_cast<_Const_Link_type>(__x)); }
-
       static _Base_ptr
-      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
-
-      static _Const_Base_ptr
-      _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
+      _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      { return __x->_M_left; }
 
       static _Base_ptr
-      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
-
-      static _Const_Base_ptr
-      _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
+      _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      { return __x->_M_right; }
 
     public:
-      typedef _Rb_tree_iterator<value_type>       iterator;
-      typedef _Rb_tree_const_iterator<value_type> const_iterator;
+      typedef typename _Node_traits::_Iterator		iterator;
+      typedef typename _Node_traits::_Const_iterator	const_iterator;
 
       typedef std::reverse_iterator<iterator>       reverse_iterator;
       typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
@@ -846,7 +1421,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_(_Base_ptr __x, _Base_ptr __y, _Arg&& __v, _NodeGen&);
 
       iterator
-      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Link_type __z);
+      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Base_ptr __z);
 
       template<typename _Arg>
 	iterator
@@ -857,10 +1432,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_equal_lower(_Arg&& __x);
 
       iterator
-      _M_insert_lower_node(_Base_ptr __p, _Link_type __z);
+      _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z);
 
       iterator
-      _M_insert_equal_lower_node(_Link_type __z);
+      _M_insert_equal_lower_node(_Base_ptr __z);
 #else
       template<typename _NodeGen>
 	iterator
@@ -879,22 +1454,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       enum { __as_lvalue, __as_rvalue };
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
-	_M_copy(_Link_type, _Base_ptr, _NodeGen&);
+	_Base_ptr
+	_M_copy(_Base_ptr, _Base_ptr, _NodeGen&);
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
+	_Base_ptr
 	_M_copy(const _Rb_tree& __x, _NodeGen& __gen)
 	{
-	  _Link_type __root =
-	    _M_copy<_MoveValues>(__x._M_mbegin(), _M_end(), __gen);
-	  _M_leftmost() = _S_minimum(__root);
-	  _M_rightmost() = _S_maximum(__root);
+	  _Base_ptr __root =
+	    _M_copy<_MoveValues>(__x._M_begin(), _M_end(), __gen);
+	  _M_leftmost() = _Node_base::_S_minimum(__root);
+	  _M_rightmost() = _Node_base::_S_maximum(__root);
 	  _M_impl._M_node_count = __x._M_impl._M_node_count;
 	  return __root;
 	}
 
-      _Link_type
+      _Base_ptr
       _M_copy(const _Rb_tree& __x)
       {
 	_Alloc_node __an(*this);
@@ -902,22 +1477,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       void
-      _M_erase(_Link_type __x);
+      _M_erase(_Base_ptr __x);
 
-      iterator
-      _M_lower_bound(_Link_type __x, _Base_ptr __y,
-		     const _Key& __k);
-
-      const_iterator
-      _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _Base_ptr
+      _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k) const;
 
-      iterator
-      _M_upper_bound(_Link_type __x, _Base_ptr __y,
-		     const _Key& __k);
-
-      const_iterator
-      _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _Base_ptr
+      _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k) const;
 
     public:
@@ -935,7 +1502,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x)
       : _M_impl(__x._M_impl)
       {
-	if (__x._M_root() != 0)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -947,7 +1514,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x, const allocator_type& __a)
       : _M_impl(__x._M_impl._M_key_compare, _Node_allocator(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -966,7 +1533,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a, false_type)
       : _M_impl(__x._M_impl._M_key_compare, std::move(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_move_data(__x, false_type{});
       }
 
@@ -974,9 +1541,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a)
       noexcept( noexcept(
 	_Rb_tree(std::declval<_Rb_tree&&>(), std::declval<_Node_allocator&&>(),
-		 std::declval<typename _Alloc_traits::is_always_equal>())) )
+		 std::declval<typename _Node_alloc_traits::is_always_equal>())) )
       : _Rb_tree(std::move(__x), std::move(__a),
-		 typename _Alloc_traits::is_always_equal{})
+		 typename _Node_alloc_traits::is_always_equal{})
       { }
 #endif
 
@@ -1001,11 +1568,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       end() _GLIBCXX_NOEXCEPT
-      { return iterator(&this->_M_impl._M_header); }
+      { return iterator(_M_end()); }
 
       const_iterator
       end() const _GLIBCXX_NOEXCEPT
-      { return const_iterator(&this->_M_impl._M_header); }
+      { return const_iterator(_M_end()); }
 
       reverse_iterator
       rbegin() _GLIBCXX_NOEXCEPT
@@ -1033,7 +1600,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       size_type
       max_size() const _GLIBCXX_NOEXCEPT
-      { return _Alloc_traits::max_size(_M_get_Node_allocator()); }
+      { return _Node_alloc_traits::max_size(_M_get_Node_allocator()); }
 
       void
       swap(_Rb_tree& __t)
@@ -1194,7 +1761,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	const_iterator __result = __position;
 	++__result;
 	_M_erase_aux(__position);
-	return __result._M_const_cast();
+	return iterator(__result._M_node);
       }
 
       // LWG 2059.
@@ -1235,7 +1802,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       erase(const_iterator __first, const_iterator __last)
       {
 	_M_erase_aux(__first, __last);
-	return __last._M_const_cast();
+	return iterator(__last._M_node);
       }
 #else
       void
@@ -1266,19 +1833,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       lower_bound(const key_type& __k)
-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
+      { return iterator(_M_lower_bound(_M_begin(), _M_end(), __k)); }
 
       const_iterator
       lower_bound(const key_type& __k) const
-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
+      {
+	return const_iterator
+	  (_M_lower_bound(_M_begin(), _M_end(), __k));
+      }
 
       iterator
       upper_bound(const key_type& __k)
-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
+      { return iterator(_M_upper_bound(_M_begin(), _M_end(), __k)); }
 
       const_iterator
       upper_bound(const key_type& __k) const
-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
+      {
+	return const_iterator
+	  (_M_upper_bound(_M_begin(), _M_end(), __k));
+      }
 
       pair<iterator, iterator>
       equal_range(const key_type& __k);
@@ -1293,7 +1866,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_find_tr(const _Kt& __k)
 	{
 	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_find_tr(__k)._M_const_cast();
+	  return iterator(__const_this->_M_find_tr(__k)._M_node);
 	}
 
       template<typename _Kt,
@@ -1301,7 +1874,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	const_iterator
 	_M_find_tr(const _Kt& __k) const
 	{
-	  auto __j = _M_lower_bound_tr(__k);
+	  const_iterator __j(_M_lower_bound_tr(__k));
 	  if (__j != end() && _M_impl._M_key_compare(__k, _S_key(__j._M_node)))
 	    __j = end();
 	  return __j;
@@ -1318,21 +1891,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename _Kt,
 	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	iterator
-	_M_lower_bound_tr(const _Kt& __k)
-	{
-	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_lower_bound_tr(__k)._M_const_cast();
-	}
-
-      template<typename _Kt,
-	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	const_iterator
+	_Base_ptr
 	_M_lower_bound_tr(const _Kt& __k) const
 	{
 	  auto __x = _M_begin();
 	  auto __y = _M_end();
-	  while (__x != 0)
+	  while (__x)
 	    if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	      {
 		__y = __x;
@@ -1340,26 +1904,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      }
 	    else
 	      __x = _S_right(__x);
-	  return const_iterator(__y);
+	  return __y;
 	}
 
       template<typename _Kt,
 	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	iterator
-	_M_upper_bound_tr(const _Kt& __k)
-	{
-	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_upper_bound_tr(__k)._M_const_cast();
-	}
-
-      template<typename _Kt,
-	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	const_iterator
+	_Base_ptr
 	_M_upper_bound_tr(const _Kt& __k) const
 	{
 	  auto __x = _M_begin();
 	  auto __y = _M_end();
-	  while (__x != 0)
+	  while (__x)
 	    if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	      {
 		__y = __x;
@@ -1367,7 +1922,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      }
 	    else
 	      __x = _S_right(__x);
-	  return const_iterator(__y);
+	  return __y;
 	}
 
       template<typename _Kt,
@@ -1377,7 +1932,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  const _Rb_tree* __const_this = this;
 	  auto __ret = __const_this->_M_equal_range_tr(__k);
-	  return { __ret.first._M_const_cast(), __ret.second._M_const_cast() };
+	  return
+	    { iterator(__ret.first._M_node), iterator(__ret.second._M_node) };
 	}
 
       template<typename _Kt,
@@ -1385,7 +1941,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	pair<const_iterator, const_iterator>
 	_M_equal_range_tr(const _Kt& __k) const
 	{
-	  auto __low = _M_lower_bound_tr(__k);
+	  const_iterator __low(_M_lower_bound_tr(__k));
 	  auto __high = __low;
 	  auto& __cmp = _M_impl._M_key_compare;
 	  while (__high != end() && !__cmp(__k, _S_key(__high._M_node)))
@@ -1401,7 +1957,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #if __cplusplus >= 201103L
       _Rb_tree&
       operator=(_Rb_tree&&)
-      noexcept(_Alloc_traits::_S_nothrow_move()
+      noexcept(_Node_alloc_traits::_S_nothrow_move()
 	       && is_nothrow_move_assignable<_Compare>::value);
 
       template<typename _Iterator>
@@ -1449,8 +2005,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    auto __res = _M_get_insert_unique_pos(__nh._M_key());
 	    if (__res.second)
 	      {
-		__ret.position
-		  = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+		__ret.position = _M_insert_node(__res.first, __res.second,
+						__nh._M_ptr->_M_base_ptr());
 		__nh.release();
 		__ret.inserted = true;
 	      }
@@ -1476,9 +2032,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
 	    auto __res = _M_get_insert_equal_pos(__nh._M_key());
 	    if (__res.second)
-	      __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+	      __ret = _M_insert_node(__res.first, __res.second,
+				     __nh._M_ptr->_M_base_ptr());
 	    else
-	      __ret = _M_insert_equal_lower_node(__nh._M_ptr);
+	      __ret = _M_insert_equal_lower_node(__nh._M_ptr->_M_base_ptr());
 	    __nh.release();
 	  }
 	return __ret;
@@ -1497,7 +2054,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    auto __res = _M_get_insert_hint_unique_pos(__hint, __nh._M_key());
 	    if (__res.second)
 	      {
-		__ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+		__ret = _M_insert_node(__res.first, __res.second,
+				       __nh._M_ptr->_M_base_ptr());
 		__nh.release();
 	      }
 	    else
@@ -1518,9 +2076,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
 	    auto __res = _M_get_insert_hint_equal_pos(__hint, __nh._M_key());
 	    if (__res.second)
-	      __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+	      __ret = _M_insert_node(__res.first, __res.second,
+				     __nh._M_ptr->_M_base_ptr());
 	    else
-	      __ret = _M_insert_equal_lower_node(__nh._M_ptr);
+	      __ret = _M_insert_equal_lower_node(__nh._M_ptr->_M_base_ptr());
 	    __nh.release();
 	  }
 	return __ret;
@@ -1530,10 +2089,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       node_type
       extract(const_iterator __pos)
       {
-	auto __ptr = _Rb_tree_rebalance_for_erase(
-	    __pos._M_const_cast()._M_node, _M_impl._M_header);
+	auto __ptr = _Node_traits::_S_rebalance_for_erase
+	  (__pos._M_node, _M_impl._M_header);
 	--_M_impl._M_node_count;
-	return { static_cast<_Link_type>(__ptr), _M_get_Node_allocator() };
+	return
+	  { __ptr->template _M_node_ptr<_Node_ptr>(), _M_get_Node_allocator() };
       }
 
       /// Extract a node.
@@ -1567,11 +2127,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr = _Node_traits::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
-		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+		  _M_insert_node(__res.first, __res.second, __ptr);
 		}
 	    }
 	}
@@ -1589,11 +2148,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr = _Node_traits::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
-		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+		  _M_insert_node(__res.first, __res.second, __ptr);
 		}
 	    }
 	}
@@ -1666,7 +2224,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
 	_Rb_tree& _M_t;
-	_Link_type _M_node;
+	_Base_ptr _M_node;
       };
 #endif // C++11
     };
@@ -1704,7 +2262,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_move_assign(_Rb_tree& __x, true_type)
     {
       clear();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	_M_move_data(__x, true_type());
       std::__alloc_on_move(_M_get_Node_allocator(),
 			   __x._M_get_Node_allocator());
@@ -1723,7 +2281,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // structure.
       _Reuse_or_alloc_node __roan(*this);
       _M_impl._M_reset();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	{
 	  _M_root() = _M_copy<__as_rvalue>(__x, __roan);
 	  __x.clear();
@@ -1735,11 +2293,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     operator=(_Rb_tree&& __x)
-    noexcept(_Alloc_traits::_S_nothrow_move()
+    noexcept(_Node_alloc_traits::_S_nothrow_move()
 	     && is_nothrow_move_assignable<_Compare>::value)
     {
       _M_impl._M_key_compare = std::move(__x._M_impl._M_key_compare);
-      _M_move_assign(__x, __bool_constant<_Alloc_traits::_S_nothrow_move()>());
+      _M_move_assign(__x,
+		     __bool_constant<_Node_alloc_traits::_S_nothrow_move()>());
       return *this;
     }
 
@@ -1780,11 +2339,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  // Note that _Key may be a constant type.
 #if __cplusplus >= 201103L
-	  if (_Alloc_traits::_S_propagate_on_copy_assign())
+	  if (_Node_alloc_traits::_S_propagate_on_copy_assign())
 	    {
 	      auto& __this_alloc = this->_M_get_Node_allocator();
 	      auto& __that_alloc = __x._M_get_Node_allocator();
-	      if (!_Alloc_traits::_S_always_equal()
+	      if (!_Node_alloc_traits::_S_always_equal()
 		  && __this_alloc != __that_alloc)
 		{
 		  // Replacement allocator cannot free existing storage, we need
@@ -1798,7 +2357,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Reuse_or_alloc_node __roan(*this);
 	  _M_impl._M_reset();
 	  _M_impl._M_key_compare = __x._M_impl._M_key_compare;
-	  if (__x._M_root() != 0)
+	  if (__x._M_root())
 	    _M_root() = _M_copy<__as_lvalue>(__x, __roan);
 	}
 
@@ -1822,14 +2381,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 		 _NodeGen& __node_gen)
       {
-	bool __insert_left = (__x != 0 || __p == _M_end()
+	bool __insert_left = (__x || __p == _M_end()
 			      || _M_impl._M_key_compare(_KeyOfValue()(__v),
 							_S_key(__p)));
 
-	_Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
+	_Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
 
-	_Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				      this->_M_impl._M_header);
+	_Node_traits::_S_insert_and_rebalance
+	  (__insert_left, __z, __p, this->_M_impl._M_header);
 	++_M_impl._M_node_count;
 	return iterator(__z);
       }
@@ -1851,10 +2410,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _KeyOfValue()(__v)));
 
-      _Link_type __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
-
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Base_ptr __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
+      _Node_traits::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -1872,9 +2430,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_insert_equal_lower(const _Val& __v)
 #endif
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = !_M_impl._M_key_compare(_S_key(__x), _KeyOfValue()(__v)) ?
@@ -1886,12 +2444,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Key, typename _Val, typename _KoV,
 	   typename _Compare, typename _Alloc>
     template<bool _MoveValues, typename _NodeGen>
-      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Link_type
+      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Base_ptr
       _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::
-      _M_copy(_Link_type __x, _Base_ptr __p, _NodeGen& __node_gen)
+      _M_copy(_Base_ptr __x, _Base_ptr __p, _NodeGen& __node_gen)
       {
 	// Structural copy. __x and __p must be non-null.
-	_Link_type __top = _M_clone_node<_MoveValues>(__x, __node_gen);
+	_Base_ptr __top = _M_clone_node<_MoveValues>(__x, __node_gen);
 	__top->_M_parent = __p;
 
 	__try
@@ -1902,9 +2460,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __p = __top;
 	    __x = _S_left(__x);
 
-	    while (__x != 0)
+	    while (__x)
 	      {
-		_Link_type __y = _M_clone_node<_MoveValues>(__x, __node_gen);
+		_Base_ptr __y = _M_clone_node<_MoveValues>(__x, __node_gen);
 		__p->_M_left = __y;
 		__y->_M_parent = __p;
 		if (__x->_M_right)
@@ -1926,13 +2484,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     void
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_erase(_Link_type __x)
+    _M_erase(_Base_ptr __x)
     {
       // Erase without rebalancing.
-      while (__x != 0)
+      while (__x)
 	{
 	  _M_erase(_S_right(__x));
-	  _Link_type __y = _S_left(__x);
+	  _Base_ptr __y = _S_left(__x);
 	  _M_drop_node(__x);
 	  __x = __y;
 	}
@@ -1941,65 +2499,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::iterator
+		      _Compare, _Alloc>::_Base_ptr
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Link_type __x, _Base_ptr __y,
-		   const _Key& __k)
-    {
-      while (__x != 0)
-	if (!_M_impl._M_key_compare(_S_key(__x), __k))
-	  __y = __x, __x = _S_left(__x);
-	else
-	  __x = _S_right(__x);
-      return iterator(__y);
-    }
-
-  template<typename _Key, typename _Val, typename _KeyOfValue,
-	   typename _Compare, typename _Alloc>
-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::const_iterator
-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	  __y = __x, __x = _S_left(__x);
 	else
 	  __x = _S_right(__x);
-      return const_iterator(__y);
-    }
-
-  template<typename _Key, typename _Val, typename _KeyOfValue,
-	   typename _Compare, typename _Alloc>
-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::iterator
-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Link_type __x, _Base_ptr __y,
-		   const _Key& __k)
-    {
-      while (__x != 0)
-	if (_M_impl._M_key_compare(__k, _S_key(__x)))
-	  __y = __x, __x = _S_left(__x);
-	else
-	  __x = _S_right(__x);
-      return iterator(__y);
+      return __y;
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::const_iterator
+		      _Compare, _Alloc>::_Base_ptr
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	  __y = __x, __x = _S_left(__x);
 	else
 	  __x = _S_right(__x);
-      return const_iterator(__y);
+      return __y;
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2011,9 +2537,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k)
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
 	    __x = _S_right(__x);
@@ -2021,13 +2547,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Link_type __xu(__x);
+	      _Base_ptr __xu(__x);
 	      _Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
-	      return pair<iterator,
-			  iterator>(_M_lower_bound(__x, __y, __k),
-				    _M_upper_bound(__xu, __yu, __k));
+	      return make_pair(iterator(_M_lower_bound(__x, __y, __k)),
+			       iterator(_M_upper_bound(__xu, __yu, __k)));
 	    }
 	}
       return pair<iterator, iterator>(iterator(__y),
@@ -2043,9 +2568,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k) const
     {
-      _Const_Link_type __x = _M_begin();
-      _Const_Base_ptr __y = _M_end();
-      while (__x != 0)
+      _Base_ptr __x = _M_begin();
+      _Base_ptr __y = _M_end();
+      while (__x)
 	{
 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
 	    __x = _S_right(__x);
@@ -2053,13 +2578,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Const_Link_type __xu(__x);
-	      _Const_Base_ptr __yu(__y);
+	      _Base_ptr __xu(__x);
+	      _Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
-	      return pair<const_iterator,
-			  const_iterator>(_M_lower_bound(__x, __y, __k),
-					  _M_upper_bound(__xu, __yu, __k));
+	      return make_pair(const_iterator(_M_lower_bound(__x, __y, __k)),
+			       const_iterator(_M_upper_bound(__xu, __yu, __k)));
 	    }
 	}
       return pair<const_iterator, const_iterator>(const_iterator(__y),
@@ -2073,12 +2597,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     swap(_Rb_tree& __t)
     _GLIBCXX_NOEXCEPT_IF(__is_nothrow_swappable<_Compare>::value)
     {
-      if (_M_root() == 0)
+      if (!_M_root())
 	{
-	  if (__t._M_root() != 0)
+	  if (__t._M_root())
 	    _M_impl._M_move_data(__t._M_impl);
 	}
-      else if (__t._M_root() == 0)
+      else if (!__t._M_root())
 	__t._M_impl._M_move_data(_M_impl);
       else
 	{
@@ -2093,8 +2617,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // No need to swap header's color as it does not change.
       std::swap(this->_M_impl._M_key_compare, __t._M_impl._M_key_compare);
 
-      _Alloc_traits::_S_on_swap(_M_get_Node_allocator(),
-				__t._M_get_Node_allocator());
+      _Node_alloc_traits::_S_on_swap(_M_get_Node_allocator(),
+				     __t._M_get_Node_allocator());
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2107,10 +2631,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_unique_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       bool __comp = true;
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __comp = _M_impl._M_key_compare(__k, _S_key(__x));
@@ -2126,7 +2650,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
       if (_M_impl._M_key_compare(_S_key(__j._M_node), __k))
 	return _Res(__x, __y);
-      return _Res(__j._M_node, 0);
+      return _Res(__j._M_node, _Base_ptr());
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2139,9 +2663,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_equal_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = _M_impl._M_key_compare(__k, _S_key(__x)) ?
@@ -2209,44 +2733,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_hint_unique_pos(const_iterator __position,
 				  const key_type& __k)
     {
-      iterator __pos = __position._M_const_cast();
       typedef pair<_Base_ptr, _Base_ptr> _Res;
 
       // end()
-      if (__pos._M_node == _M_end())
+      if (__position._M_node == _M_end())
 	{
 	  if (size() > 0
 	      && _M_impl._M_key_compare(_S_key(_M_rightmost()), __k))
-	    return _Res(0, _M_rightmost());
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else
 	    return _M_get_insert_unique_pos(__k);
 	}
-      else if (_M_impl._M_key_compare(__k, _S_key(__pos._M_node)))
+      else if (_M_impl._M_key_compare(__k, _S_key(__position._M_node)))
 	{
 	  // First, try before...
-	  iterator __before = __pos;
-	  if (__pos._M_node == _M_leftmost()) // begin()
+	  iterator __before(__position._M_node);
+	  if (__position._M_node == _M_leftmost()) // begin()
 	    return _Res(_M_leftmost(), _M_leftmost());
 	  else if (_M_impl._M_key_compare(_S_key((--__before)._M_node), __k))
 	    {
-	      if (_S_right(__before._M_node) == 0)
-		return _Res(0, __before._M_node);
+	      if (!_S_right(__before._M_node))
+		return _Res(_Base_ptr(), __before._M_node);
 	      else
-		return _Res(__pos._M_node, __pos._M_node);
+		return _Res(__position._M_node, __position._M_node);
 	    }
 	  else
 	    return _M_get_insert_unique_pos(__k);
 	}
-      else if (_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
+      else if (_M_impl._M_key_compare(_S_key(__position._M_node), __k))
 	{
 	  // ... then try after.
-	  iterator __after = __pos;
-	  if (__pos._M_node == _M_rightmost())
-	    return _Res(0, _M_rightmost());
+	  iterator __after(__position._M_node);
+	  if (__position._M_node == _M_rightmost())
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else if (_M_impl._M_key_compare(__k, _S_key((++__after)._M_node)))
 	    {
-	      if (_S_right(__pos._M_node) == 0)
-		return _Res(0, __pos._M_node);
+	      if (!_S_right(__position._M_node))
+		return _Res(_Base_ptr(), __position._M_node);
 	      else
 		return _Res(__after._M_node, __after._M_node);
 	    }
@@ -2255,7 +2778,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
       else
 	// Equivalent keys.
-	return _Res(__pos._M_node, 0);
+	return _Res(__position._M_node, _Base_ptr());
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2294,30 +2817,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
     {
-      iterator __pos = __position._M_const_cast();
       typedef pair<_Base_ptr, _Base_ptr> _Res;
 
       // end()
-      if (__pos._M_node == _M_end())
+      if (__position._M_node == _M_end())
 	{
 	  if (size() > 0
 	      && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
-	    return _Res(0, _M_rightmost());
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else
 	    return _M_get_insert_equal_pos(__k);
 	}
-      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
+      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
 	{
 	  // First, try before...
-	  iterator __before = __pos;
-	  if (__pos._M_node == _M_leftmost()) // begin()
+	  iterator __before(__position._M_node);
+	  if (__position._M_node == _M_leftmost()) // begin()
 	    return _Res(_M_leftmost(), _M_leftmost());
 	  else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
 	    {
-	      if (_S_right(__before._M_node) == 0)
-		return _Res(0, __before._M_node);
+	      if (!_S_right(__before._M_node))
+		return _Res(_Base_ptr(), __before._M_node);
 	      else
-		return _Res(__pos._M_node, __pos._M_node);
+		return _Res(__position._M_node, __position._M_node);
 	    }
 	  else
 	    return _M_get_insert_equal_pos(__k);
@@ -2325,18 +2847,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       else
 	{
 	  // ... then try after.
-	  iterator __after = __pos;
-	  if (__pos._M_node == _M_rightmost())
-	    return _Res(0, _M_rightmost());
+	  iterator __after(__position._M_node);
+	  if (__position._M_node == _M_rightmost())
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else if (!_M_impl._M_key_compare(_S_key((++__after)._M_node), __k))
 	    {
-	      if (_S_right(__pos._M_node) == 0)
-		return _Res(0, __pos._M_node);
+	      if (!_S_right(__position._M_node))
+		return _Res(_Base_ptr(), __position._M_node);
 	      else
 		return _Res(__after._M_node, __after._M_node);
 	    }
 	  else
-	    return _Res(0, 0);
+	    return _Res(_Base_ptr(), _Base_ptr());
 	}
     }
 
@@ -2373,15 +2895,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Link_type __z)
+    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Base_ptr __z)
     -> iterator
     {
-      bool __insert_left = (__x != 0 || __p == _M_end()
+      bool __insert_left = (__x || __p == _M_end()
 			    || _M_impl._M_key_compare(_S_key(__z),
 						      _S_key(__p)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Node_traits::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -2390,15 +2912,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_lower_node(_Base_ptr __p, _Link_type __z)
+    _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z)
     -> iterator
     {
       bool __insert_left = (__p == _M_end()
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _S_key(__z)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Node_traits::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -2407,12 +2929,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_equal_lower_node(_Link_type __z)
+    _M_insert_equal_lower_node(_Base_ptr __z)
     -> iterator
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = !_M_impl._M_key_compare(_S_key(__x), _S_key(__z)) ?
@@ -2487,10 +3009,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     _M_erase_aux(const_iterator __position)
     {
-      _Link_type __y =
-	static_cast<_Link_type>(_Rb_tree_rebalance_for_erase
-				(const_cast<_Base_ptr>(__position._M_node),
-				 this->_M_impl._M_header));
+      _Base_ptr __y = _Node_traits::_S_rebalance_for_erase
+	(__position._M_node, this->_M_impl._M_header);
       _M_drop_node(__y);
       --_M_impl._M_node_count;
     }
@@ -2527,7 +3047,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     find(const _Key& __k)
     {
-      iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
+      iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
       return (__j == end()
 	      || _M_impl._M_key_compare(__k,
 					_S_key(__j._M_node))) ? end() : __j;
@@ -2540,7 +3060,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     find(const _Key& __k) const
     {
-      const_iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
+      const_iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
       return (__j == end()
 	      || _M_impl._M_key_compare(__k,
 					_S_key(__j._M_node))) ? end() : __j;
@@ -2574,9 +3094,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       unsigned int __len = _Rb_tree_black_count(_M_leftmost(), _M_root());
       for (const_iterator __it = begin(); __it != end(); ++__it)
 	{
-	  _Const_Link_type __x = static_cast<_Const_Link_type>(__it._M_node);
-	  _Const_Link_type __L = _S_left(__x);
-	  _Const_Link_type __R = _S_right(__x);
+	  _Base_ptr __x = __it._M_node;
+	  _Base_ptr __L = _S_left(__x);
+	  _Base_ptr __R = _S_right(__x);
 
 	  if (__x->_M_color == _S_red)
 	    if ((__L && __L->_M_color == _S_red)
@@ -2592,9 +3112,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    return false;
 	}
 
-      if (_M_leftmost() != _Rb_tree_node_base::_S_minimum(_M_root()))
+      if (_M_leftmost() != _Node_base::_S_minimum(_M_root()))
 	return false;
-      if (_M_rightmost() != _Rb_tree_node_base::_S_maximum(_M_root()))
+      if (_M_rightmost() != _Node_base::_S_maximum(_M_root()))
 	return false;
       return true;
     }
diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..3d83738f86b
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
@@ -0,0 +1,37 @@
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::map<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::map<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( v.begin()->second == 0 );
+  VERIFY( ++v.begin() == v.end() );
+  v.insert({ T { 1 }, 1 });
+  VERIFY( v.size() == 2 );
+  VERIFY( v.find(T()) != v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..41689ef43b7
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
@@ -0,0 +1,33 @@
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multimap<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::multimap<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..589e6b0a617
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
@@ -0,0 +1,32 @@
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multiset<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::multiset<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..bbcf32b20be
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
@@ -0,0 +1,32 @@
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::set<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::set<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
  
Jonathan Wakely Dec. 4, 2024, 9:48 p.m. UTC | #17
On 04/12/24 19:27 +0100, François Dumont wrote:
>Hi
>
>I've completed the synchronization with your equivalent PR for 
>std::list so here is the updated patch.
>
>PR updated a couple of days ago.
>
>Note that I've started to rework the patch for the same in _Hashtable.

Great, thanks.

>François
>
>
>On 30/11/2024 18:21, Jonathan Wakely wrote:
>>
>>
>>On Sat, 30 Nov 2024, 13:52 François Dumont, <frs.dumont@gmail.com> wrote:
>>
>>    Hi
>>
>>    I've applied all your comments below and the ones you did on the
>>    PR directly.
>>
>>    When all new types totally seperated from the legacy types
>>    _Rb_tree_node_traits is indeed useless.
>>
>>    Regarding _Rb_tree_helpers I got rid of it but move the
>>    insert-and-rebalance and rebalance-for-erase function to
>>    _Node_traits<>.
>>
>>    PR updated:
>>
>>    https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
>>
>>    libstdc++: Add fancy pointer support in map and set Support fancy
>>    allocator pointer type in std::_Rb_tree<>. In case of fancy
>>    pointer type the container is now storing the pointer to
>>    __rb_tree::_Node<_ValPtr> as a pointer to
>>    __rb_tree::_Node_base<_VoidPtr>. Many methods are adapted to take
>>    and return _Base_ptr in place of _Link_type which has been renamed
>>    into _Node_ptr. As all node are stored as _Base_ptr have all
>>    methods working with this type and remove _Const_Base_ptr and
>>    _Const_Node_ptr and all methods associated with it.
>>
>>
>>Sounds good! I'll take another look on Monday.
>>
>>
>>    libstdc++-v3/ChangeLog: * include/bits/stl_tree.h
>>    [_GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE]: New macro to control usage
>>    of the code required to support fancy allocator pointer type.
>>    (_Rb_tree_node_base::_Const_Base_ptr): Remove.
>>    (_Rb_tree_node_base::_S_minimum, _Rb_tree_node_base::_S_maximum):
>>    Remove overloads for _Const_Base_ptr.
>>    (_Rb_tree_node_base::_M_base_ptr()): New.
>>    (_Rb_tree_node_base::_M_node_ptr<_NodePtr>()): New.
>>    (_Rb_tree_node::_Link_type): Rename into...
>>    (_Rb_tree_node::_Node_ptr): ...this. (__rb_tree::_Node_base<>):
>>    New. (__rb_tree::_Header<>): New. (__rb_tree::_Node<>): New.
>>    (_Rb_tree_increment(const _Rb_tree_node_base*)): Remove
>>    declaration. (_Rb_tree_decrement(const _Rb_tree_node_base*)):
>>    Remove declaration. (_Rb_tree_iterator<>::_Link_type): Rename
>>    into... (_Rb_tree_iterator<>::_Node_ptr): ...this.
>>    (_Rb_tree_const_iterator<>::_Link_type): Rename into...
>>    (_Rb_tree_const_iterator<>::_Node_ptr): ...this.
>>    (_Rb_tree_const_iterator<>::_M_const_cast): Remove.
>>    (_Rb_tree_const_iterator<>::_M_node): Change type into _Base_ptr.
>>    (__rb_tree::_Iterator<>): New. (__rb_tree::_Node_traits<>): New.
>>    (_Rb_tree<>::_Node_base, _Rb_tree::_Node_type): New.
>>    (_Rb_tree<>::_Link_type): Rename into... (_Rb_tree<>::_Node_ptr):
>>    ...this. (_Rb_tree<>::_Const_Base_ptr,
>>    _Rb_tree<>::_Const_Node_ptr): Remove. (_Rb_tree<>): Adapt to
>>    generalize usage of _Base_ptr in place of former _Link_type now
>>    _Node_ptr. (_Rb_tree<>::_M_mbegin): Remove.
>>    (_Rb_tree<>::_S_left(_Const_Base_ptr)): Remove.
>>    (_Rb_tree<>::_S_right(_Const_Base_ptr)): Remove.
>>    (_Rb_tree<>::_S_maximum(_Const_Base_ptr)): Remove.
>>    (_Rb_tree<>::_S_minimum(_Const_Base_ptr)): Remove. *
>>    testsuite/23_containers/map/allocator/ext_ptr.cc: New test case. *
>>    testsuite/23_containers/multimap/allocator/ext_ptr.cc: New test
>>    case. * testsuite/23_containers/multiset/allocator/ext_ptr.cc: New
>>    test case. * testsuite/23_containers/set/allocator/ext_ptr.cc: New
>>    test case.
>>
>>    I've tested it in C++98/C++17/C++20 with default
>>    _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE and with it 9001.
>>
>>    François
>>
>>    On 18/11/2024 17:39, Jonathan Wakely wrote:
>>>    On 18/11/24 06:57 +0100, François Dumont wrote:
>>>>    Hi
>>>>
>>>>    Here is a new proposal with all the cleanup regarding _Const_Base_ptr
>>>>    that makes support of allocator's fancy pointer type simpler.
>>>>
>>>>    Also submitted as PR:
>>>>
>>>>    https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
>>>>
>>>>       libstdc++: Add fancy pointer support in map and set
>>>>
>>>>        Support fancy allocator pointer type instd::_Rb_tree<>.
>>>>
>>>>        In case of fancy pointer type the container is now storing the
>>>>    pointer to
>>>>        _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.
>>>>
>>>>        Many methods are adapted to take and return _Base_ptr in place of
>>>>    _Link_type
>>>>        which has been renamed into _Node_ptr.
>>>>
>>>>        As all node are stored as _Base_ptr have all methods working with
>>>>    this type
>>>>        and remove _Const_Base_ptr and all methods associated to it.
>>>>
>>>>        libstdc++-v3/ChangeLog:
>>>>
>>>>                * include/bits/stl_set.h (std::set<>::upper_bound<>(const
>>>>    _Kt&) const): Fix
>>>>                decltype typo to use const_iterator.
>>>>                * include/bits/stl_tree.h
>>>>                (_Rb_tree_ptr_traits<>): New.
>>>>                (_Rb_tree_pnode_base<>): New.
>>>>                (_Rb_tree_node_base): Inherit from latter.
>>>>                (_Rb_tree_node_base::_Const_Base_ptr): Remove.
>>>>                (_Rb_tree_node_base::_S_minimum(_Const_Base_ptr)): Remove.
>>>>                (_Rb_tree_node_base::_S_maximum(_Const_Base_ptr)): Remove.
>>>>                (_Rb_tree_pheader): New.
>>>>                (_Rb_tree_header): Inherit from latter.
>>>>                (_Rb_tree_node_val): New.
>>>>                (_Rb_tree_node): Inherit from latter.
>>>>                (_Rb_tree_pnode): New.
>>>>                (_Rb_tree_iterator<>::_Link_type): Rename into...
>>>>                (_Rb_tree_iterator<>::_Node_ptr): ...this.
>>>>                (_Rb_tree_const_iterator<>::_Link_type): Rename into...
>>>>                (_Rb_tree_const_iterator<>::_Node_ptr): ...this.
>>>>                (_Rb_tree_const_iterator<>::_M_node): Change type into
>>>>    _Base_ptr.
>>>>                (_Rb_tree_const_iterator<>::_M_const_cast): Remove.
>>>>                (_Rb_tree_helpers<>): New.
>>>>                (_Rb_tree_piterator): New.
>>>>                (_Rb_tree_const_piterator): New.
>>>>                (_Rb_tree_node_traits<>): New.
>>>>                (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
>>>>                (_Rb_tree<>::_Const_Base_ptr): Remove.
>>>>                (_Rb_tree): Adapt to generalize usage of _Base_ptr in
>>>>    place of _Link_type.
>>>>                (_Rb_tree<>::_M_mbegin): Remove.
>>>>                (_Rb_tree<>::_S_left(_Const_Base_ptr)): Remove.
>>>>                (_Rb_tree<>::_S_right(_Const_Base_ptr)): Remove.
>>>>                (_Rb_tree<>::_S_maximum(_Const_Base_ptr)): Remove.
>>>>                (_Rb_tree<>::_S_minimum(_Const_Base_ptr)): Remove.
>>>>                * testsuite/23_containers/map/allocator/ext_ptr.cc: New
>>>>    test case.
>>>>                * testsuite/23_containers/multimap/allocator/ext_ptr.cc:
>>>>    New test case.
>>>>                * testsuite/23_containers/multiset/allocator/ext_ptr.cc:
>>>>    New test case.
>>>>                * testsuite/23_containers/set/allocator/ext_ptr.cc: New
>>>>    test case.
>>>>
>>>>    Tested under Linux x64.
>>>>
>>>>
>>>>    Note that I've also run the 23_containers tests on map, multimap,
>>>>    multiset and set tweaking implementation
>>>>    so that new types are being used when C++11 or later even if allocator
>>>>    pointer type is a C pointer.
>>>    Please take a look at the _GLIBCXX_USE_ALLOC_PTR_xxx macros in
>>>    https://gcc.gnu.org/pipermail/gcc-patches/2024-November/669128.html
>>>    andhttps://gcc.gnu.org/pipermail/gcc-patches/2024-November/669127.html
>>>    Those macros allow the fancy pointer support to be disabled, in case
>>>    it will break anything for current users. Maybe more importantly, it
>>>    allows the new node types to be used always, even for T*. That means
>>>    we can run the entire testsuite using the new nodes without tweaking
>>>    the implementation.
>>>
>>>    Please consider a _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE macro (or some
>>>    other name).
>>>
>>>>    Ok to commit ?
>>>>
>>>>    François
>>>>
>>>>    On 12/11/2024 15:56, Jonathan Wakely wrote:
>>>>>    On Mon, 4 Nov 2024 at 21:34, François Dumont<frs.dumont@gmail.com> <mailto:frs.dumont@gmail.com> wrote:
>>>>>>    On 04/11/2024 19:45, Jonathan Wakely wrote:
>>>>>>>    On Mon, 4 Nov 2024 at 18:30, François Dumont<frs.dumont@gmail.com> <mailto:frs.dumont@gmail.com> wrote:
>>>>>>>>    On 21/10/2024 06:56, François Dumont wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>>    On 17/10/2024 23:11, Jonathan Wakely wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>    On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely<jwakely.gcc@gmail.com> <mailto:jwakely.gcc@gmail.com> wrote:
>>>>>>>>>    On Thu, 17 Oct 2024 at 20:52, François Dumont<frs.dumont@gmail.com> <mailto:frs.dumont@gmail.com> wrote:
>>>>>>>>>>    Here is an updated version that compiles, I think, all your feedbacks. It's much cleaner indeed.
>>>>>>>>>    Thanks, I'll take a look tomorrow.
>>>>>>>>>
>>>>>>>>>>    It's also tested in C++98/17/23.
>>>>>>>>>>
>>>>>>>>>>    I'm surprised that we do not need to consider potentialallocator::const_pointer.
>>>>>>>>>    Do you mean consider the case whereAlloc::const_pointer is not the same type as rebinding 'pointer' to a const element type?
>>>>>>>>    Yes, exactly.
>>>>>>>>
>>>>>>>>
>>>>>>>>>    We don't need to consider that because we never get a 'const_pointer' from the allocator, and we never need to pass a 'const_pointer' to the allocator. The allocator's 'allocate' and 'deallocate' members both work with the 'pointer' type, so we only need to use that type when interacting with the allocator. For all the other uses, such as _Const_Node_ptr, what we need is a pointer-to-const that's compatible with the allocator's pointer type. It doesn't actually matter if it's the same type as allocator_traits<Alloc>::const_pointer, because we don't need
>>>>>>>>    Sorry, I sent the email before finishing that thought!
>>>>>>>>
>>>>>>>>    ... we don't need to pass a const_pointer to anything, we only need it for the container's own purposes.
>>>>>>>>
>>>>>>>>    But thinking about it some more, do we even need a const-pointer for the container?  Currently the const_iterator stores a const-pointer, and some members like _M_root() and _M_leftmost() return a const-pointer. But they don't need to. The nodes are all pointed to by a non-const _Base_ptr, none of the storage managed by the container is const.
>>>>>>>>
>>>>>>>>    Except _M_impl._M_header in a const context.
>>>>>>>>
>>>>>>>>    Using const_cast would result in a similar UB that you fixed on _GLIBCXX_DEBUG containers, no ?
>>>>>>>    We only use a pointer to _M_header for the past-the-end iterator,
>>>>>>>    which is not dereferenceable. It's OK to do the const_cast, it's not
>>>>>>>    OK to write something through that pointer/reference if the object is
>>>>>>>    really const. But you can't write through a past-the-end pointer
>>>>>>>    anyway.
>>>>>>>
>>>>>>    One last question then to complete this work, is it fine to change
>>>>>>    _Rb_tree_const_iterator::_M_node to _Base_ptr ? No abi issue in doing so ?
>>>>>    I thought I'd sent a reply to this question, but I don't see it in the
>>>>>    archives, sorry.
>>>>>
>>>>>    So changing from _Const_Base_ptr to _Base_ptr? Yes, I think that's OK.
>>>>>
>>>>    diff --git a/libstdc++-v3/include/bits/stl_set.h b/libstdc++-v3/include/bits/stl_set.h
>>>>    index c0eb4dbf65f..3d17626f330 100644
>>>>    --- a/libstdc++-v3/include/bits/stl_set.h
>>>>    +++ b/libstdc++-v3/include/bits/stl_set.h
>>>>    @@ -875,7 +875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>>           template<typename _Kt>
>>>>         auto
>>>>         upper_bound(const _Kt& __x) const
>>>>    -    -> decltype(iterator(_M_t._M_upper_bound_tr(__x)))
>>>>    +    -> decltype(const_iterator(_M_t._M_upper_bound_tr(__x)))
>>>>         { return const_iterator(_M_t._M_upper_bound_tr(__x)); }
>>>>    #endif
>>>>           ///@}
>>>>    diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
>>>>    index bc27e191e8b..c6cba99726d 100644
>>>>    --- a/libstdc++-v3/include/bits/stl_tree.h
>>>>    +++ b/libstdc++-v3/include/bits/stl_tree.h
>>>>    @@ -96,44 +96,100 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>
>>>>       enum _Rb_tree_color { _S_red = false, _S_black = true };
>>>>
>>>>    -  struct _Rb_tree_node_base
>>>>    -  {
>>>>    -    typedef _Rb_tree_node_base* _Base_ptr;
>>>>    -    typedef const _Rb_tree_node_base* _Const_Base_ptr;
>>>>    -
>>>>    -    _Rb_tree_color    _M_color;
>>>>    -    _Base_ptr        _M_parent;
>>>>    -    _Base_ptr        _M_left;
>>>>    -    _Base_ptr        _M_right;
>>>>    -
>>>>    -    static _Base_ptr
>>>>    -    _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>>    +#if __cplusplus >= 201103L
>>>>    +  template<typename _Ptr>
>>>>    +    struct _Rb_tree_ptr_traits
>>>>         {
>>>>    -      while (__x->_M_left != 0) __x = __x->_M_left;
>>>>    -      return __x;
>>>>    -    }
>>>>    +      template<typename _Tp>
>>>>    +    struct __rebind
>>>>    +    { using __type = __ptr_rebind<_Ptr, _Tp>; };
>>>    This only seems to be used in one place, can you just use __ptr_rebind
>>>    directly in that one place?
>>>
>>>>    +
>>>>    +      template<typename _BasePtr>
>>>>    +    static _Ptr
>>>>    +    _S_ptr_to(_BasePtr __this) noexcept
>>>>    +    { return pointer_traits<_Ptr>::pointer_to(*__this); }
>>>    This is also only needed in exactly one place. I avoided needing this
>>>    function by adding _M_base_ptr() to the node base classes.
>>>
>>>    The whole of this _Rb_tree_ptr_traits class template seems unnecessary
>>>    to me. Just inline the code directly into the places it's needed.
>>>    You're not optimizing anything by replacing
>>>    pointer_traits<T*>::pointer_to with
>>>    _Rb_tree_ptr_traits<T*>::_S_ptr_to, it's still a class template
>>>    specialization and a function call.
>>>
>>>>    +
>>>>    +      template<typename _BasePtr, typename _UpPtr>
>>>>    +    static _UpPtr
>>>>    +    _S_up_ptr_to(_BasePtr __this) noexcept
>>>    pointer_to is not guaranteed to be noexcept.
>>>
>>>    This appears to be a downcast, i.e. a base-to-derived cast, having
>>>    "up" in the name is a bit confusing.
>>>
>>>>    +    {
>>>>    +      using __ptr_traits = pointer_traits<_UpPtr>;
>>>>    +      using __up_type = typename __ptr_traits::element_type;
>>>>    +      auto __up_this = static_cast<__up_type*>(__this);
>>>>    +      return __ptr_traits::pointer_to(*__up_this);
>>>>    +    }
>>>>    +    };
>>>>    +#else
>>>>    +  template<typename _Ptr>
>>>>    +    struct _Rb_tree_ptr_traits;
>>>>    +#endif
>>>>
>>>>    -    static _Const_Base_ptr
>>>>    -    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>>    +  template<typename _Tp>
>>>>    +    struct _Rb_tree_ptr_traits<_Tp*>
>>>>         {
>>>>    -      while (__x->_M_left != 0) __x = __x->_M_left;
>>>>    -      return __x;
>>>>    -    }
>>>>    +      template<typename>
>>>>    +    struct __rebind
>>>>    +    { typedef _Tp* __type; };
>>>>    +
>>>>    +      template<typename _BasePtr>
>>>>    +    static _Tp*
>>>>    +    _S_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
>>>>    +    { return static_cast<_Tp*>(__this); }
>>>>    +
>>>>    +      template<typename _BasePtr, typename _UpPtr>
>>>>    +    static _UpPtr
>>>>    +    _S_up_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
>>>>    +    { return static_cast<_UpPtr>(__this); }
>>>>    +    };
>>>>
>>>>    -    static _Base_ptr
>>>>    -    _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>>    +  template<typename _BasePtr>
>>>>    +    struct _Rb_tree_pnode_base
>>>>         {
>>>>    -      while (__x->_M_right != 0) __x = __x->_M_right;
>>>>    -      return __x;
>>>>    -    }
>>>>    +      typedef typename _Rb_tree_ptr_traits<_BasePtr>::template
>>>>    +    __rebind<_Rb_tree_pnode_base>::__type _Base_ptr;
>>>    The names _BasePtr and _Base_ptr are very similar, and confusing.
>>>    It looks like sometimes the template argument that is substituted for
>>>    _BasePtr is sometimes a derived class (_Rb_tree_node_base) and
>>>    sometimes is _ValPtr. It's never a "base" pointer. Please rename it.
>>>
>>>>    -    static _Const_Base_ptr
>>>>    -    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>>    -    {
>>>>    -      while (__x->_M_right != 0) __x = __x->_M_right;
>>>>    -      return __x;
>>>>    -    }
>>>>    -  };
>>>>    +      _Base_ptr
>>>>    +      _M_self_ptr() _GLIBCXX_NOEXCEPT
>>>    Why do we need a non-const overload? The const one will work for all
>>>    cases, right?
>>>
>>>    I added a comment to my list patches pointing out that it's not
>>>    const-correct, but is safe as long as only used carefully.
>>>
>>>
>>>>    +      { return _Rb_tree_ptr_traits<_Base_ptr>::_S_ptr_to(this); }
>>>>    +
>>>>    +      _Base_ptr
>>>>    +      _M_self_ptr() const _GLIBCXX_NOEXCEPT
>>>>    +      {
>>>>    +    _Rb_tree_pnode_base* __self = const_cast<_Rb_tree_pnode_base*>(this);
>>>>    +    return __self->_M_self_ptr();
>>>>    +      }
>>>>    +
>>>>    +      template<typename _NodePtr>
>>>>    +    _NodePtr
>>>>    +    _M_node_ptr() _GLIBCXX_NOEXCEPT
>>>>    +    {
>>>>    +      return _Rb_tree_ptr_traits<_Base_ptr>::template
>>>>    +        _S_up_ptr_to<_Rb_tree_pnode_base*, _NodePtr>(this);
>>>>    +    }
>>>>    +
>>>>    +      _Rb_tree_color    _M_color;
>>>>    +      _Base_ptr        _M_parent;
>>>>    +      _Base_ptr        _M_left;
>>>>    +      _Base_ptr        _M_right;
>>>>    +
>>>>    +      static _Base_ptr
>>>>    +      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>>    +      {
>>>>    +    while (__x->_M_left) __x = __x->_M_left;
>>>>    +    return __x;
>>>>    +      }
>>>>    +
>>>>    +      static _Base_ptr
>>>>    +      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>>    +      {
>>>>    +    while (__x->_M_right) __x = __x->_M_right;
>>>>    +    return __x;
>>>>    +      }
>>>>    +    };
>>>>    +
>>>>    +  struct _Rb_tree_node_base
>>>>    +    : _Rb_tree_pnode_base<_Rb_tree_node_base*>
>>>>    +  { };
>>>    This approaches means that the existing specializations of RB trees
>>>    all get twice as many base classes. I don't see an ABI problem, and it
>>>    should generate the same code for optimized builds, but increasing the
>>>    inheritance depth and the number of class template instantiations
>>>    gives the compiler more work to do, compiling slower, and generating
>>>    additional debug info (and maybe RTTI?)
>>>
>>>    Please take a look at how I did it for the linked lists, where
>>>    specializations for non-fancy pointers, e.g. _List_node<T>, are mostly
>>>    unchanged. They don't get any new base classes, just some new typedefs
>>>    and new accessors like:
>>>
>>>    _Base_ptr _M_base() { return this; }
>>>    const _List_node* _M_base() const { return this; }
>>>
>>>    The _List_node_traits class template is used bystd::list etc. to
>>>    select whether to use _List_node<Alloc::value_type> or
>>>    __list::_Node<Alloc::pointer>. All the fancy pointer support is in the
>>>    new type hierarchy, __list::_Node_base, __list::_Node, and
>>>    __list::_Iterator. The old types like _List_node don't really change.
>>>
>>>    I found that using the __list namespace with new types reduced a lot
>>>    of confusion I had between the very similar names _List_node and
>>>    _List_pnode (or _List_ptr_node, or other names like that).
>>>
>>>    Maybe we could call it _List::_Node instead of __list::_Node, so it's
>>>    a bit closer to _List_node but still distinguishable (and would
>>>    produce very slightly shorter mangled names). I didn't do that,
>>>    because our convention is to name impl namespaces as __foo not _Foo.
>>>
>>>    That pattern could naturally be extended to __tree::_Node (or
>>>    _Tree::_Node) as well.
>>>
>>>>       // Helper type offering value initialization guarantee on the compare functor.
>>>>       template<typename _Key_compare>
>>>>    @@ -163,81 +219,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>         };
>>>>
>>>>       // Helper type to manage default initialization of node count and header.
>>>>    -  struct _Rb_tree_header
>>>>    -  {
>>>>    -    _Rb_tree_node_base    _M_header;
>>>>    -    size_t        _M_node_count; // Keeps track of size of tree.
>>>>    -
>>>>    -    _Rb_tree_header() _GLIBCXX_NOEXCEPT
>>>>    +  template<typename _NodeBase>
>>>>    +    struct _Rb_tree_pheader
>>>>         {
>>>>    -      _M_header._M_color = _S_red;
>>>>    -      _M_reset();
>>>>    -    }
>>>>    +    private:
>>>>    +      typedef typename _NodeBase::_Base_ptr        _Base_ptr;
>>>>    +
>>>>    +    public:
>>>>    +      _NodeBase        _M_header;
>>>>    +      size_t        _M_node_count; // Keeps track of size of tree.
>>>>    +
>>>>    +      _Rb_tree_pheader() _GLIBCXX_NOEXCEPT
>>>>    +      {
>>>>    +    _M_header._M_color = _S_red;
>>>>    +    _M_reset();
>>>>    +      }
>>>>
>>>>    #if __cplusplus >= 201103L
>>>>    -    _Rb_tree_header(_Rb_tree_header&& __x) noexcept
>>>>    -    {
>>>>    -      if (__x._M_header._M_parent != nullptr)
>>>>    -    _M_move_data(__x);
>>>>    -      else
>>>>    -    {
>>>>    -      _M_header._M_color = _S_red;
>>>>    -      _M_reset();
>>>>    -    }
>>>>    -    }
>>>>    +      _Rb_tree_pheader(_Rb_tree_pheader&& __x) noexcept
>>>>    +      {
>>>>    +    if (__x._M_header._M_parent)
>>>>    +      _M_move_data(__x);
>>>>    +    else
>>>>    +      {
>>>>    +        _M_header._M_color = _S_red;
>>>>    +        _M_reset();
>>>>    +      }
>>>>    +      }
>>>>    #endif
>>>>
>>>>    -    void
>>>>    -    _M_move_data(_Rb_tree_header& __from)
>>>>    -    {
>>>>    -      _M_header._M_color = __from._M_header._M_color;
>>>>    -      _M_header._M_parent = __from._M_header._M_parent;
>>>>    -      _M_header._M_left = __from._M_header._M_left;
>>>>    -      _M_header._M_right = __from._M_header._M_right;
>>>>    -      _M_header._M_parent->_M_parent = &_M_header;
>>>>    -      _M_node_count = __from._M_node_count;
>>>>    -
>>>>    -      __from._M_reset();
>>>>    -    }
>>>>    +      void
>>>>    +      _M_move_data(_Rb_tree_pheader& __from)
>>>>    +      {
>>>>    +    _M_header._M_color = __from._M_header._M_color;
>>>>    +    _M_header._M_parent = __from._M_header._M_parent;
>>>>    +    _M_header._M_left = __from._M_header._M_left;
>>>>    +    _M_header._M_right = __from._M_header._M_right;
>>>>    +    _M_header._M_parent->_M_parent = _M_header._M_self_ptr();
>>>>    +    _M_node_count = __from._M_node_count;
>>>>    +
>>>>    +    __from._M_reset();
>>>>    +      }
>>>>
>>>>    -    void
>>>>    -    _M_reset()
>>>>    -    {
>>>>    -      _M_header._M_parent = 0;
>>>>    -      _M_header._M_left = &_M_header;
>>>>    -      _M_header._M_right = &_M_header;
>>>>    -      _M_node_count = 0;
>>>>    -    }
>>>>    -  };
>>>>    +      void
>>>>    +      _M_reset()
>>>>    +      {
>>>>    +    _M_header._M_parent = _Base_ptr();
>>>>    +    _M_header._M_left = _M_header._M_right = _M_header._M_self_ptr();
>>>>    +    _M_node_count = 0;
>>>>    +      }
>>>>    +    };
>>>>    +
>>>>    +  struct _Rb_tree_header : _Rb_tree_pheader<_Rb_tree_node_base>
>>>>    +  { };
>>>>
>>>>       template<typename _Val>
>>>>    -    struct _Rb_tree_node : public _Rb_tree_node_base
>>>>    +    struct _Rb_tree_node_val
>>>>         {
>>>>    -      typedef _Rb_tree_node<_Val>* _Link_type;
>>>>    -
>>>>    #if __cplusplus < 201103L
>>>>           _Val _M_value_field;
>>>>
>>>>    +      __attribute__((__always_inline__))
>>>>           _Val*
>>>>           _M_valptr()
>>>>           { returnstd::__addressof(_M_value_field); }
>>>>
>>>>    +      __attribute__((__always_inline__))
>>>>           const _Val*
>>>>           _M_valptr() const
>>>>           { returnstd::__addressof(_M_value_field); }
>>>>    #else
>>>>           __gnu_cxx::__aligned_membuf<_Val> _M_storage;
>>>    For the new __list::_Node type I took the opportunity to replace
>>>    __aligned_membuf with a union { _Val _M_data; }; which is simpler and
>>>    produces the same code. We could probably make that change for the
>>>    existing node types, I think it's safe, but making it only for the new
>>>    _Node type means the old node type isn't affected.
>>>
>>>
>>>>    +      [[__gnu__::__always_inline__]]
>>>>           _Val*
>>>>           _M_valptr()
>>>>           { return _M_storage._M_ptr(); }
>>>>
>>>>    +      [[__gnu__::__always_inline__]]
>>>>           const _Val*
>>>>           _M_valptr() const
>>>>           { return _M_storage._M_ptr(); }
>>>>    #endif
>>>>         };
>>>>
>>>>    +  template<typename _Val>
>>>>    +    struct _Rb_tree_node
>>>>    +    : _Rb_tree_node_base
>>>>    +    , _Rb_tree_node_val<_Val>
>>>>    +    {
>>>>    +      typedef _Rb_tree_node<_Val>* _Node_ptr;
>>>>    +    };
>>>>    +
>>>>    +#if __cplusplus >= 201103L
>>>>    +  template<typename _ValPtr>
>>>>    +    struct _Rb_tree_pnode
>>>>    +    : _Rb_tree_pnode_base<_ValPtr>
>>>    This makes _Rb_tree_pnode_base depend on the value_type, which means
>>>    you instantiate a different specialization of _Rb_tree_pnode_base for
>>>    every _Rb_tree. There's no point even having a common base class if
>>>    every specialization is different. You might as well just put the
>>>    _M_parent/_M_left/_M_right pointers in _Rb_tree_pnode directly.
>>>
>>>    The point of the node_base class is to have a common base that is not
>>>    dependent on the value type, so thatstd::set<int> andstd::set<long>
>>>    andstd::set<int, Cmp> all use the same node base. The way this patch
>>>    works,std::set<int> will use _Rb_tree_pnode_base<int*> and
>>>    std::set<long> will use _Rb_tree_pnode_base<long*>.
>>>
>>>    This is why my patch for lists uses __ptr_rebind<_ValPtr, void> as the
>>>    template argument to _Node_base, so that they all use
>>>    _Node_base<void*>.
>>>
>>>    Obviously for a fancy pointer, it will be a different specialization,
>>>    sostd::list<int, A<int>> uses _Node_base<A::void_pointer>, but
>>>    std::list<long, A<long>> uses the same _Node_base<A::void_pointer>.
>>>
>>>>    +    , _Rb_tree_node_val<typenamestd::pointer_traits<_ValPtr>::element_type>
>>>>    +    {
>>>>    +      using _Node_ptr = __ptr_rebind<_ValPtr, _Rb_tree_pnode>;
>>>>    +    };
>>>>    +#endif
>>>>    +
>>>>       _GLIBCXX_PURE _Rb_tree_node_base*
>>>>       _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
>>>>
>>>    [...]
>>>
>>>>    +#if __cplusplus >= 201103L
>>>>    +  template<typename _ValPtr>
>>>>    +    struct _Rb_tree_piterator
>>>>    +    {
>>>>    +      using __ptr_traits = pointer_traits<_ValPtr>;
>>>>    +      using value_type = typename __ptr_traits::element_type;
>>>>    +      using reference = value_type&;
>>>>    +      using pointer = value_type*;
>>>>    +
>>>>    +      typedef bidirectional_iterator_tag iterator_category;
>>>>    +      typedef ptrdiff_t             difference_type;
>>>>    +
>>>>    +      typedef _Rb_tree_piterator            _Self;
>>>>    +      typedef _Rb_tree_pnode_base<_ValPtr>        _Node_base;
>>>>    +      typedef _Rb_tree_pnode<_ValPtr>            _Node_type;
>>>>    +      typedef typename _Node_type::_Base_ptr        _Base_ptr;
>>>>    +      typedef typename _Node_type::_Node_ptr        _Node_ptr;
>>>>    +      typedef _Rb_tree_helpers<_Node_base>        _Tree_helpers;
>>>>    +
>>>>    +      _Rb_tree_piterator() _GLIBCXX_NOEXCEPT
>>>>    +      : _M_node() { }
>>>>    +
>>>>    +      explicit
>>>>    +      _Rb_tree_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>>    +      : _M_node(__x) { }
>>>>    +
>>>>    +      reference
>>>>    +      operator*() const _GLIBCXX_NOEXCEPT
>>>>    +      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>>>>    +
>>>>    +      pointer
>>>>    +      operator->() const _GLIBCXX_NOEXCEPT
>>>>    +      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>>>>    +
>>>>    +      _Self&
>>>>    +      operator++() _GLIBCXX_NOEXCEPT
>>>>    +      {
>>>>    +    _M_node = _Tree_helpers::_Increment(_M_node);
>>>>    +    return *this;
>>>>    +      }
>>>>    +
>>>>    +      _Self
>>>>    +      operator++(int) _GLIBCXX_NOEXCEPT
>>>>    +      {
>>>>    +    _Self __tmp = *this;
>>>>    +    _M_node = _Tree_helpers::_Increment(_M_node);
>>>>    +    return __tmp;
>>>>    +      }
>>>>    +
>>>>    +      _Self&
>>>>    +      operator--() _GLIBCXX_NOEXCEPT
>>>>    +      {
>>>>    +    _M_node = _Tree_helpers::_Decrement(_M_node);
>>>>    +    return *this;
>>>>    +      }
>>>>    +
>>>>    +      _Self
>>>>    +      operator--(int) _GLIBCXX_NOEXCEPT
>>>>    +      {
>>>>    +    _Self __tmp = *this;
>>>>    +    _M_node = _Tree_helpers::_Decrement(_M_node);
>>>>    +    return __tmp;
>>>>    +      }
>>>>    +
>>>>    +      friend bool
>>>>    +      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>>>>    +      { return __x._M_node == __y._M_node; }
>>>>    +
>>>>    +#if ! __cpp_lib_three_way_comparison
>>>>    +      friend bool
>>>>    +      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>>>>    +      { return __x._M_node != __y._M_node; }
>>>>    +#endif
>>>>    +
>>>>    +      _Base_ptr _M_node;
>>>>    +    };
>>>>    +
>>>>    +  template<typename _ValPtr>
>>>>    +    struct _Rb_tree_const_piterator
>>>    For the linked lists, I didn't add two new iterator types, just
>>>    _Iterator<_Const, _ValPtr> which is used for iterator and
>>>    const_iterator.
>>>
>>>    It seemed nice not to duplicate the iterator type, since the iterator
>>>    and const_iterator are 95% the same. If we don't need to support
>>>    C++98, we can write a single iterator implementation.
>>>
>>>    If you have a single iterator implementation, you probably don't need
>>>    the _Rb_tree_helpers.
>>>
>>>    There would still be uses of insert-and-rebalance and
>>>    rebalance-for-erase, but those could just be new overloads for fancy
>>>    pointers. I don't think we need the _Rb_tree_helpers class template.
>>>
>>>
>>>>    +    {
>>>>    +      using __ptr_traits = pointer_traits<_ValPtr>;
>>>>    +      using value_type = typename __ptr_traits::element_type;
>>>>    +      using reference = const value_type&;
>>>>    +      using pointer = const value_type*;
>>>>    +
>>>>    +      typedef _Rb_tree_piterator<_ValPtr>        iterator;
>>>>    +      typedef typenameiterator::_Base_ptr        _Base_ptr;
>>>>    +
>>>>    +      typedef bidirectional_iterator_tag iterator_category;
>>>>    +      typedef ptrdiff_t             difference_type;
>>>>    +
>>>>    +      typedef _Rb_tree_const_piterator            _Self;
>>>>    +      typedef _Rb_tree_pnode_base<_ValPtr>        _Node_base;
>>>>    +      typedef _Rb_tree_pnode<_ValPtr>            _Node_type;
>>>>    +      typedef _Rb_tree_helpers<_Node_base>        _Tree_helpers;
>>>>    +
>>>>    +      _Rb_tree_const_piterator() _GLIBCXX_NOEXCEPT
>>>>    +      : _M_node() { }
>>>>    +
>>>>    +      explicit
>>>>    +      _Rb_tree_const_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>>>>    +      : _M_node(__x) { }
>>>>    +
>>>>    +      _Rb_tree_const_piterator(const iterator& __it) _GLIBCXX_NOEXCEPT
>>>>    +      : _M_node(__it._M_node) { }
>>>>    +
>>>>    +      reference
>>>>    +      operator*() const _GLIBCXX_NOEXCEPT
>>>>    +      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>>>>    +
>>>>    +      pointer
>>>>    +      operator->() const _GLIBCXX_NOEXCEPT
>>>>    +      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
>>>>    +
>>>>    +      _Self&
>>>>    +      operator++() _GLIBCXX_NOEXCEPT
>>>>    +      {
>>>>    +    _M_node = _Tree_helpers::_Increment(_M_node);
>>>    You need the iterator increment code here, but you can define it
>>>    inline without the _Increment helper.
>>>
>>>>    +    return *this;
>>>>    +      }
>>>>    +
>>>>    +      _Self
>>>>    +      operator++(int) _GLIBCXX_NOEXCEPT
>>>>    +      {
>>>>    +    _Self __tmp = *this;
>>>>    +    _M_node = _Tree_helpers::_Increment(_M_node);
>>>    This could just use ++*this. So if there's no separate const iterator
>>>    implementation, there's no more use of _Increment.
>>>
>>>>    +    return __tmp;
>>>>    +      }
>>>>    +
>>>>    +      _Self&
>>>>    +      operator--() _GLIBCXX_NOEXCEPT
>>>>    +      {
>>>>    +    _M_node = _Tree_helpers::_Decrement(_M_node);
>>>>    +    return *this;
>>>>    +      }
>>>>    +
>>>>    +      _Self
>>>>    +      operator--(int) _GLIBCXX_NOEXCEPT
>>>>    +      {
>>>>    +    _Self __tmp = *this;
>>>>    +    _M_node = _Tree_helpers::_Decrement(_M_node);
>>>>    +    return __tmp;
>>>>    +      }
>>>>    +
>>>>    +      friend bool
>>>>    +      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>>>>    +      { return __x._M_node == __y._M_node; }
>>>>    +
>>>>    +#if ! __cpp_lib_three_way_comparison
>>>>    +      friend bool
>>>>    +      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>>>>    +      { return __x._M_node != __y._M_node; }
>>>>    +#endif
>>>>    +
>>>>    +      _Base_ptr _M_node;
>>>>    +    };
>>>>    +#endif // C++11
>>>>    +
>>>>    +#if __cplusplus >= 201103L
>>>>    +  template<typename _ValPtr>
>>>>    +    struct _Rb_tree_node_traits
>>>>    +    {
>>>>    +      using _Node_base        = _Rb_tree_pnode_base<_ValPtr>;
>>>>    +      using _Node_type        = _Rb_tree_pnode<_ValPtr>;
>>>>    +      using _Header_t        = _Rb_tree_pheader<_Node_base>;
>>>>    +      using _Iterator_t        = _Rb_tree_piterator<_ValPtr>;
>>>>    +      using _Const_iterator_t    = _Rb_tree_const_piterator<_ValPtr>;
>>>>    +    };
>>>>    +#else
>>>>    +  template<typename _ValPtr>
>>>>    +    struct _Rb_tree_node_traits;
>>>>    +#endif
>>>>    +
>>>>    +  template<typename _Val>
>>>>    +    struct _Rb_tree_node_traits<_Val*>
>>>>    +    {
>>>>    +      typedef _Rb_tree_node_base        _Node_base;
>>>>    +      typedef _Rb_tree_node<_Val>        _Node_type;
>>>>    +      typedef _Rb_tree_header            _Header_t;
>>>>    +      typedef _Rb_tree_iterator<_Val>        _Iterator_t;
>>>>    +      typedef _Rb_tree_const_iterator<_Val>    _Const_iterator_t;
>>>>    +    };
>>>>
>>>>    #if __cplusplus > 201402L
>>>>       template<typename _Tree1, typename _Cmp2>
>>>>    @@ -424,16 +1068,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>            typename _Compare, typename _Alloc = allocator<_Val> >
>>>>         class _Rb_tree
>>>>         {
>>>>    +      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::pointer _ValPtr;
>>>>    +      typedef _Rb_tree_node_traits<_ValPtr> _Node_traits;
>>>>    +
>>>>    +      typedef typename _Node_traits::_Node_base        _Node_base;
>>>>    +      typedef typename _Node_traits::_Node_type        _Node_type;
>>>>    +
>>>>           typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
>>>>    -    rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
>>>>    +    rebind<_Node_type>::other _Node_allocator;
>>>>
>>>>           typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
>>>>
>>>>         protected:
>>>>    -      typedef _Rb_tree_node_base*         _Base_ptr;
>>>>    -      typedef const _Rb_tree_node_base*     _Const_Base_ptr;
>>>>    -      typedef _Rb_tree_node<_Val>*         _Link_type;
>>>>    -      typedef const _Rb_tree_node<_Val>*    _Const_Link_type;
>>>>    +      typedef typename _Node_base::_Base_ptr         _Base_ptr;
>>>>    +      typedef typename _Node_type::_Node_ptr         _Node_ptr;
>>>    Accessing a member of _Node_type here requires _Node_type to be
>>>    complete, which requires _Tp to be complete. That's allowed by the
>>>    standard, becausestd::map andstd::set don't support incomplete types
>>>    (unlike vector and the linked lists).
>>>
>>>    But we don't really ned to instantiate _Node_type here, we could use
>>>    _Alloc_traits::pointer instead.
>>>
>>>
>>>    [...]
>>>
>>>>    @@ -1826,10 +2427,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>                       || _M_impl._M_key_compare(_KeyOfValue()(__v),
>>>>                                 _S_key(__p)));
>>>>
>>>>    -    _Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
>>>>    +    _Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
>>>    I think this relies on implicit conversion from _Node_ptr to
>>>    _Base_ptr, which is not necessarily possible. It works for the
>>>    _Pointer_adapter type in <ext/pointer.h> because it has a
>>>    template<typename U> _Pointer_adapter(_Pointer_adapter<U>) constructor
>>>    that supports implicit conversions.
>>>
>>>    There seems to be a few other places with this problem. Please take a
>>>    look at the Pointer type in the new test for my list and forward_list
>>>    fancy ptr patches (that Pointer should probably be added to the
>>>    testsuite/util/testsuite_allocator.h header for reuse).
>>>
>>>    I solved these problems by adding a _M_base_ptr() function to the
>>>    node_base type, which means that instead of relying on an implicit
>>>    conversion from node_ptr to _Base_ptr you call node_ptr->_M_base_ptr()
>>>    to get a _Base_ptr.
>>>
>>>>    @@ -2255,7 +2821,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>         }
>>>>           else
>>>>         // Equivalent keys.
>>>>    -    return _Res(__pos._M_node, 0);
>>>>    +    return _Res(__position._M_node, 0);
>>>>         }
>>>>
>>>>       template<typename _Key, typename _Val, typename _KeyOfValue,
>>>>    @@ -2294,11 +2860,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>         _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>>>>         _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
>>>>         {
>>>>    -      iterator __pos = __position._M_const_cast();
>>>    This removes __pos ...
>>>
>>>>           typedef pair<_Base_ptr, _Base_ptr> _Res;
>>>>
>>>>           // end()
>>>>    -      if (__pos._M_node == _M_end())
>>>>    +      if (__position._M_node == _M_end())
>>>>         {
>>>>           if (size() > 0
>>>>               && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
>>>>    @@ -2306,18 +2871,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>           else
>>>>             return _M_get_insert_equal_pos(__k);
>>>>         }
>>>>    -      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
>>>>    +      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
>>>>         {
>>>>           // First, try before...
>>>>           iterator __before = __pos;
>>>    But it's still used here.
>>>
>>>>    -      if (__pos._M_node == _M_leftmost()) // begin()
>>>>    +      if (__position._M_node == _M_leftmost()) // begin()
>>>>             return _Res(_M_leftmost(), _M_leftmost());
>>>>           else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
>>>>             {
>>>>               if (_S_right(__before._M_node) == 0)
>>>>             return _Res(0, __before._M_node);
>>>>               else
>>>>    -        return _Res(__pos._M_node, __pos._M_node);
>>>>    +        return _Res(__position._M_node, __position._M_node);
>>>>             }
>>>>           else
>>>>             return _M_get_insert_equal_pos(__k);
>>>>    @@ -2326,12 +2891,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>         {
>>>>           // ... then try after.
>>>>           iterator __after = __pos;
>>>    And here.
>>>
>>>    I think they should both be __position._M_const_cast().
>>>
>>>
>>>>    diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
>>>>    new file mode 100644
>>>>    index 00000000000..758beedccfd
>>>>    --- /dev/null
>>>>    +++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
>>>>    @@ -0,0 +1,44 @@
>>>>    +// { dg-do run { target c++11 } }
>>>>    +
>>>>    +#include <map>
>>>>    +#include <memory>
>>>>    +#include <testsuite_hooks.h>
>>>>    +#include <testsuite_allocator.h>
>>>>    +
>>>>    +struct T { int i; };
>>>>    +
>>>>    +bool operator<(const T& l, const T& r)
>>>>    +{ return l.i < r.i; }
>>>>    +
>>>>    +struct H
>>>    This struct H occurs in several tests but seems to be unused.
>>>
>>>>    +{
>>>>    +std::size_t
>>>>    +  operator()(const T& t) const noexcept
>>>>    +  { return t.i; }
>>>>    +};
>>>>    +
>>>>    +struct L :std::less<T>
>>>>    +{ };
>>>>    +
>>>>    +using __gnu_test::CustomPointerAlloc;
>>>>    +
>>>>    +template classstd::map<T, int, L,
>>>>    +            CustomPointerAlloc<std::pair<const T, int>>>;
>>>>    +
>>>>    +void test01()
>>>>    +{
>>>>    +  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
>>>>    +  typedefstd::map<T, int, L, alloc_type> test_type;
>>>>    +  test_type v;
>>>>    +  v.insert({ T(), 0 });
>>>>    +  VERIFY( v.begin()->second == 0 );
>>>>    +  VERIFY( ++v.begin() == v.end() );
>>>>    +  v.insert({ T { 1 }, 1 });
>>>>    +  VERIFY( v.size() == 2 );
>>>>    +  VERIFY( v.find(T()) != v.end() );
>>>>    +}
>>>>    +
>>>>    +int main()
>>>>    +{
>>>>    +  test01();
>>>>    +}
>>>    I'd be interested on your opinion on my patches for fancy pointer
>>>    support in the linked lists. I think it's cleaner and requires fewer
>>>    changes to the existing specializations.
>>>
>>>    My changes can also be disabled by a macro (to avoid breaking any
>>>    users who are somehow managing to use allocators-with-fancy-pointers
>>>    with RB trees today, which could be useful unless that's completely
>>>    broken today), and to be enabled for non-fancy pointers (for test
>>>    coverage).
>>>

>diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
>index bc27e191e8b..bed7266c688 100644
>--- a/libstdc++-v3/include/bits/stl_tree.h
>+++ b/libstdc++-v3/include/bits/stl_tree.h
>@@ -66,6 +66,7 @@
> #include <bits/allocator.h>
> #include <bits/stl_function.h>
> #include <bits/cpp_type_traits.h>
>+#include <bits/ptr_traits.h>
> #include <ext/alloc_traits.h>
> #if __cplusplus >= 201103L
> # include <ext/aligned_buffer.h>
>@@ -74,6 +75,13 @@
> # include <bits/node_handle.h>
> #endif
>
>+#if __cplusplus < 201103L
>+# undef _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
>+# define _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE 0
>+#elif ! defined _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
>+# define _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE 1
>+#endif
>+
> namespace std _GLIBCXX_VISIBILITY(default)
> {
> _GLIBCXX_BEGIN_NAMESPACE_VERSION
>@@ -99,7 +107,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   struct _Rb_tree_node_base
>   {
>     typedef _Rb_tree_node_base* _Base_ptr;
>-    typedef const _Rb_tree_node_base* _Const_Base_ptr;
>
>     _Rb_tree_color	_M_color;
>     _Base_ptr		_M_parent;
>@@ -113,13 +120,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       return __x;
>     }
>
>-    static _Const_Base_ptr
>-    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>-    {
>-      while (__x->_M_left != 0) __x = __x->_M_left;
>-      return __x;
>-    }
>-
>     static _Base_ptr
>     _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>     {
>@@ -127,12 +127,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       return __x;
>     }
>
>-    static _Const_Base_ptr
>-    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>-    {
>-      while (__x->_M_right != 0) __x = __x->_M_right;
>-      return __x;
>-    }
>+    // This is not const-correct, but it's only used in a const access path
>+    // by std::_Rb_tree::_M_end() where the pointer is used to initialize a
>+    // const_iterator and so constness is restored.
>+    _Base_ptr
>+    _M_base_ptr() const _GLIBCXX_NOEXCEPT
>+    { return const_cast<_Rb_tree_node_base*>(this); }
>+
>+    template<typename _NodePtr>
>+      _NodePtr
>+      _M_node_ptr() _GLIBCXX_NOEXCEPT
>+      { return static_cast<_NodePtr>(this); }
>   };
>
>   // Helper type offering value initialization guarantee on the compare functor.
>@@ -213,7 +218,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   template<typename _Val>
>     struct _Rb_tree_node : public _Rb_tree_node_base
>     {
>-      typedef _Rb_tree_node<_Val>* _Link_type;
>+      typedef _Rb_tree_node<_Val>* _Node_ptr;
>
> #if __cplusplus < 201103L
>       _Val _M_value_field;
>@@ -238,18 +243,136 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> #endif
>     };
>
>+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
>+namespace __rb_tree
>+{
>+  template<typename _VoidPtr>
>+    struct _Node_base
>+    {
>+      using _Base_ptr = __ptr_rebind<_VoidPtr, _Node_base>;
>+
>+      _Rb_tree_color	_M_color;
>+      _Base_ptr		_M_parent;
>+      _Base_ptr		_M_left;
>+      _Base_ptr		_M_right;
>+
>+      static _Base_ptr
>+      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>+      {
>+	while (__x->_M_left) __x = __x->_M_left;
>+	return __x;
>+      }
>+
>+      static _Base_ptr
>+      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>+      {
>+	while (__x->_M_right) __x = __x->_M_right;
>+	return __x;
>+      }
>+
>+      // This is not const-correct, but it's only used in a const access path
>+      // by std::_Rb_tree::_M_end() where the pointer is used to initialize a
>+      // const_iterator and so constness is restored.
>+      _Base_ptr
>+      _M_base_ptr() const
>+      {
>+	return pointer_traits<_Base_ptr>::pointer_to
>+	  (*const_cast<_Node_base*>(this));
>+      }
>+
>+      template<typename _NodePtr>
>+	_NodePtr
>+	_M_node_ptr() _GLIBCXX_NOEXCEPT
>+	{
>+	  using __ptr_traits = pointer_traits<_NodePtr>;
>+	  using __node_type = typename __ptr_traits::element_type;
>+	  auto __this = static_cast<__node_type*>(this);
>+	  return __ptr_traits::pointer_to(*__this);
>+	}
>+    };
>+
>+  // Helper type to manage default initialization of node count and header.
>+  template<typename _NodeBase>
>+    struct _Header
>+    {
>+    private:
>+      using _Base_ptr =  typename _NodeBase::_Base_ptr;
>+
>+    public:
>+      _NodeBase		_M_header;
>+      size_t		_M_node_count; // Keeps track of size of tree.
>+
>+      _Header() noexcept
>+      {
>+	_M_header._M_color = _S_red;
>+	_M_reset();
>+      }
>+
>+      _Header(_Header&& __x) noexcept
>+      {
>+	if (__x._M_header._M_parent)
>+	  _M_move_data(__x);
>+	else
>+	  {
>+	    _M_header._M_color = _S_red;
>+	    _M_reset();
>+	  }
>+      }
>+
>+      void
>+      _M_move_data(_Header& __from)
>+      {
>+	_M_header._M_color = __from._M_header._M_color;
>+	_M_header._M_parent = __from._M_header._M_parent;
>+	_M_header._M_left = __from._M_header._M_left;
>+	_M_header._M_right = __from._M_header._M_right;
>+	_M_header._M_parent->_M_parent = _M_header._M_base_ptr();
>+	_M_node_count = __from._M_node_count;
>+
>+	__from._M_reset();
>+      }
>+
>+      void
>+      _M_reset()
>+      {
>+	_M_header._M_parent = _Base_ptr{};
>+	_M_header._M_left = _M_header._M_right = _M_header._M_base_ptr();
>+	_M_node_count = 0;
>+      }
>+    };
>+
>+  template<typename _ValPtr>
>+    struct _Node : public __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>
>+    {
>+      using value_type = typename pointer_traits<_ValPtr>::element_type;
>+      using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
>+
>+      union
>+      {
>+	value_type _M_storage;
>+      };
>+
>+      _Node() { }
>+      ~_Node() { }
>+      _Node(_Node&&) = delete;
>+
>+      value_type*
>+      _M_valptr()
>+      { return std::__addressof(_M_storage); }
>+
>+      value_type const*
>+      _M_valptr() const
>+      { return std::__addressof(_M_storage); }
>+    };
>+} // namespace __rb_tree
>+#endif
>+
>   _GLIBCXX_PURE _Rb_tree_node_base*
>   _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
>
>-  _GLIBCXX_PURE const _Rb_tree_node_base*
>-  _Rb_tree_increment(const _Rb_tree_node_base* __x) throw ();
>-
>   _GLIBCXX_PURE _Rb_tree_node_base*
>   _Rb_tree_decrement(_Rb_tree_node_base* __x) throw ();
>
>-  _GLIBCXX_PURE const _Rb_tree_node_base*
>-  _Rb_tree_decrement(const _Rb_tree_node_base* __x) throw ();
>-
>   template<typename _Tp>
>     struct _Rb_tree_iterator
>     {
>@@ -260,9 +383,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       typedef bidirectional_iterator_tag iterator_category;
>       typedef ptrdiff_t			 difference_type;
>
>-      typedef _Rb_tree_iterator<_Tp>		_Self;
>       typedef _Rb_tree_node_base::_Base_ptr	_Base_ptr;
>-      typedef _Rb_tree_node<_Tp>*		_Link_type;
>+      typedef _Rb_tree_node<_Tp>*		_Node_ptr;
>
>       _Rb_tree_iterator() _GLIBCXX_NOEXCEPT
>       : _M_node() { }
>@@ -273,49 +395,51 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>       reference
>       operator*() const _GLIBCXX_NOEXCEPT
>-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
>+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
>
>       pointer
>       operator->() const _GLIBCXX_NOEXCEPT
>-      { return static_cast<_Link_type> (_M_node)->_M_valptr(); }
>+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
>
>-      _Self&
>+      _Rb_tree_iterator&
>       operator++() _GLIBCXX_NOEXCEPT
>       {
> 	_M_node = _Rb_tree_increment(_M_node);
> 	return *this;
>       }
>
>-      _Self
>+      _Rb_tree_iterator
>       operator++(int) _GLIBCXX_NOEXCEPT
>       {
>-	_Self __tmp = *this;
>+	_Rb_tree_iterator __tmp = *this;
> 	_M_node = _Rb_tree_increment(_M_node);
> 	return __tmp;
>       }
>
>-      _Self&
>+      _Rb_tree_iterator&
>       operator--() _GLIBCXX_NOEXCEPT
>       {
> 	_M_node = _Rb_tree_decrement(_M_node);
> 	return *this;
>       }
>
>-      _Self
>+      _Rb_tree_iterator
>       operator--(int) _GLIBCXX_NOEXCEPT
>       {
>-	_Self __tmp = *this;
>+	_Rb_tree_iterator __tmp = *this;
> 	_M_node = _Rb_tree_decrement(_M_node);
> 	return __tmp;
>       }
>
>       friend bool
>-      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>+      operator==(const _Rb_tree_iterator& __x,
>+		 const _Rb_tree_iterator& __y) _GLIBCXX_NOEXCEPT
>       { return __x._M_node == __y._M_node; }
>
> #if ! __cpp_lib_three_way_comparison
>       friend bool
>-      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>+      operator!=(const _Rb_tree_iterator& __x,
>+		 const _Rb_tree_iterator& __y) _GLIBCXX_NOEXCEPT
>       { return __x._M_node != __y._M_node; }
> #endif
>
>@@ -334,9 +458,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       typedef bidirectional_iterator_tag iterator_category;
>       typedef ptrdiff_t			 difference_type;
>
>-      typedef _Rb_tree_const_iterator<_Tp>		_Self;
>-      typedef _Rb_tree_node_base::_Const_Base_ptr	_Base_ptr;
>-      typedef const _Rb_tree_node<_Tp>*			_Link_type;
>+      typedef _Rb_tree_node_base::_Base_ptr	_Base_ptr;
>+      typedef const _Rb_tree_node<_Tp>*		_Node_ptr;
>
>       _Rb_tree_const_iterator() _GLIBCXX_NOEXCEPT
>       : _M_node() { }
>@@ -348,55 +471,53 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _Rb_tree_const_iterator(const iterator& __it) _GLIBCXX_NOEXCEPT
>       : _M_node(__it._M_node) { }
>
>-      iterator
>-      _M_const_cast() const _GLIBCXX_NOEXCEPT
>-      { return iterator(const_cast<typename iterator::_Base_ptr>(_M_node)); }
>-
>       reference
>       operator*() const _GLIBCXX_NOEXCEPT
>-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
>+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
>
>       pointer
>       operator->() const _GLIBCXX_NOEXCEPT
>-      { return static_cast<_Link_type>(_M_node)->_M_valptr(); }
>+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
>
>-      _Self&
>+      _Rb_tree_const_iterator&
>       operator++() _GLIBCXX_NOEXCEPT
>       {
> 	_M_node = _Rb_tree_increment(_M_node);
> 	return *this;
>       }
>
>-      _Self
>+      _Rb_tree_const_iterator
>       operator++(int) _GLIBCXX_NOEXCEPT
>       {
>-	_Self __tmp = *this;
>+	_Rb_tree_const_iterator __tmp = *this;
> 	_M_node = _Rb_tree_increment(_M_node);
> 	return __tmp;
>       }
>
>-      _Self&
>+      _Rb_tree_const_iterator&
>       operator--() _GLIBCXX_NOEXCEPT
>       {
> 	_M_node = _Rb_tree_decrement(_M_node);
> 	return *this;
>       }
>
>-      _Self
>+      _Rb_tree_const_iterator
>       operator--(int) _GLIBCXX_NOEXCEPT
>       {
>-	_Self __tmp = *this;
>+	_Rb_tree_const_iterator __tmp = *this;
> 	_M_node = _Rb_tree_decrement(_M_node);
> 	return __tmp;
>       }
>
>       friend bool
>-      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>+      operator==(const _Rb_tree_const_iterator& __x,
>+		 const _Rb_tree_const_iterator& __y) _GLIBCXX_NOEXCEPT
>       { return __x._M_node == __y._M_node; }
>
> #if ! __cpp_lib_three_way_comparison
>       friend bool
>-      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>+      operator!=(const _Rb_tree_const_iterator& __x,
>+		 const _Rb_tree_const_iterator& __y) _GLIBCXX_NOEXCEPT
>       { return __x._M_node != __y._M_node; }
> #endif
>
>@@ -415,6 +536,485 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
> 			       _Rb_tree_node_base& __header) throw ();
>
>+namespace __rb_tree
>+{
>+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
>+  template<bool _Const, typename _ValPtr>
>+    struct _Iterator
>+    {
>+      template<typename _Tp>
>+	using __maybe_const = __conditional_t<_Const, const _Tp, _Tp>;
>+
>+      using __ptr_traits =	pointer_traits<_ValPtr>;
>+      using value_type =	typename __ptr_traits::element_type;
>+      using reference =		__maybe_const<value_type>&;
>+      using pointer =		__maybe_const<value_type>*;
>+
>+      using iterator_category =	bidirectional_iterator_tag;
>+      using difference_type =	ptrdiff_t;
>+
>+      using _Node = __rb_tree::_Node<_ValPtr>;
>+      using _Node_base = __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>;
>+      using _Base_ptr =	 typename _Node_base::_Base_ptr;
>+
>+      _Iterator() _GLIBCXX_NOEXCEPT
>+      : _M_node() { }
>+
>+      constexpr explicit
>+      _Iterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>+      : _M_node(__x) { }
>+
>+#if __cplusplus >= 201103L
>+      _Iterator(const _Iterator&) = default;
>+      _Iterator& operator=(const _Iterator&) = default;
>+#endif
>+
>+#ifdef __glibcxx_concepts
>+      constexpr
>+      _Iterator(const _Iterator<false, _ValPtr>& __it) requires _Const
>+#else
>+      template<bool _OtherConst,
>+	       typename = __enable_if_t<_Const && !_OtherConst>>
>+	constexpr
>+	_Iterator(const _Iterator<_OtherConst, _ValPtr>& __it)
>+#endif
>+	: _M_node(__it._M_node) { }
>+
>+      [[__nodiscard__]]
>+      reference
>+      operator*() const _GLIBCXX_NOEXCEPT
>+      { return *static_cast<_Node&>(*_M_node)._M_valptr(); }
>+
>+      [[__nodiscard__]]
>+      pointer
>+      operator->() const _GLIBCXX_NOEXCEPT
>+      { return static_cast<_Node&>(*_M_node)._M_valptr(); }
>+
>+      _GLIBCXX14_CONSTEXPR _Iterator&
>+      operator++() _GLIBCXX_NOEXCEPT
>+      {
>+	if (_M_node->_M_right)
>+	  {
>+	    _M_node = _M_node->_M_right;
>+	    while (_M_node->_M_left)
>+	      _M_node = _M_node->_M_left;
>+	  }
>+	else
>+	  {
>+	    _Base_ptr __y = _M_node->_M_parent;
>+	    while (_M_node == __y->_M_right)
>+	      {
>+		_M_node = __y;
>+		__y = __y->_M_parent;
>+	      }
>+	    if (_M_node->_M_right != __y)
>+	      _M_node = __y;
>+	  }
>+
>+	return *this;
>+      }
>+
>+      _GLIBCXX14_CONSTEXPR _Iterator
>+      operator++(int) _GLIBCXX_NOEXCEPT
>+      {
>+	_Iterator __tmp(this->_M_node);
>+	++*this;
>+	return __tmp;
>+      }
>+
>+      _GLIBCXX14_CONSTEXPR _Iterator&
>+      operator--() _GLIBCXX_NOEXCEPT
>+      {
>+	if (_M_node->_M_color == _S_red
>+	    && _M_node->_M_parent->_M_parent == _M_node)
>+	  _M_node = _M_node->_M_right;
>+	else if (_M_node->_M_left)
>+	  {
>+	    _Base_ptr __y = _M_node->_M_left;
>+	    while (__y->_M_right)
>+	      __y = __y->_M_right;
>+	    _M_node = __y;
>+	  }
>+	else
>+	  {
>+	    _Base_ptr __y = _M_node->_M_parent;
>+	    while (_M_node == __y->_M_left)
>+	      {
>+		_M_node = __y;
>+		__y = __y->_M_parent;
>+	      }
>+	    _M_node = __y;
>+	  }
>+	return *this;
>+      }
>+
>+      _GLIBCXX14_CONSTEXPR _Iterator
>+      operator--(int) _GLIBCXX_NOEXCEPT
>+      {
>+	_Iterator __tmp(this->_M_node);
>+	--*this;
>+	return __tmp;
>+      }
>+
>+      [[__nodiscard__]]
>+      friend bool
>+      operator==(const _Iterator& __x, const _Iterator& __y) _GLIBCXX_NOEXCEPT
>+      { return __x._M_node == __y._M_node; }
>+
>+#if ! __cpp_lib_three_way_comparison
>+      [[__nodiscard__]]
>+      friend bool
>+      operator!=(const _Iterator& __x, const _Iterator& __y) _GLIBCXX_NOEXCEPT
>+      { return __x._M_node != __y._M_node; }
>+#endif
>+
>+      _Base_ptr _M_node;
>+    };
>+#endif // USE_ALLOC_PTR_FOR_RB_TREE
>+
>+  // Determine the node and iterator types used by std::_Rb_tree.
>+  template<typename _Val, typename _Ptr>
>+    struct _Node_traits;
>+
>+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE <= 9000
>+  // Specialization for the simple case where the allocator's pointer type
>+  // is the same type as value_type*.
>+  // For ABI compatibility we can't change the types used for this case.
>+  template<typename _Val>
>+    struct _Node_traits<_Val, _Val*>
>+    {
>+      typedef _Rb_tree_node<_Val>		_Node;
>+      typedef _Rb_tree_node_base		_Node_base;
>+      typedef _Rb_tree_header			_Header_t;
>+      typedef _Rb_tree_iterator<_Val>		_Iterator;
>+      typedef _Rb_tree_const_iterator<_Val>	_Const_iterator;
>+
>+      __attribute__((__nonnull__))
>+      static void
>+      _S_insert_and_rebalance(const bool __insert_left,
>+			      _Node_base* __x, _Node_base* __p,
>+			      _Node_base& __header) _GLIBCXX_USE_NOEXCEPT
>+      {
>+	return _Rb_tree_insert_and_rebalance(__insert_left, __x, __p, __header);
>+      }
>+
>+      __attribute__((__nonnull__,__returns_nonnull__))
>+      static _Node_base*
>+      _S_rebalance_for_erase(_Node_base* const __z,
>+			     _Node_base& __header) _GLIBCXX_USE_NOEXCEPT
>+      { return _Rb_tree_rebalance_for_erase(__z, __header); }
>+    };
>+#endif
>+
>+#if ! _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
>+  // Always use the T* specialization.
>+  template<typename _Val, typename _Ptr>
>+    struct _Node_traits
>+    : _Node_traits<_Val, _Val*>
>+    { };
>+#else
>+  // Primary template used when the allocator uses fancy pointers.
>+  template<typename _Val, typename _ValPtr>
>+    struct _Node_traits
>+    {
>+      using _Node = __rb_tree::_Node<_ValPtr>;
>+      using _Node_base = __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>;
>+      using _Header_t = __rb_tree::_Header<_Node_base>;
>+      using _Iterator = __rb_tree::_Iterator<false, _ValPtr>;
>+      using _Const_iterator = __rb_tree::_Iterator<true, _ValPtr>;
>+
>+      using _Base_ptr = typename _Node_base::_Base_ptr;
>+
>+      static void
>+      _Rotate_left(_Base_ptr __x, _Base_ptr& __root)
>+      {
>+	const _Base_ptr __y = __x->_M_right;
>+
>+	__x->_M_right = __y->_M_left;
>+	if (__y->_M_left)
>+	  __y->_M_left->_M_parent = __x;
>+	__y->_M_parent = __x->_M_parent;
>+
>+	if (__x == __root)
>+	  __root = __y;
>+	else if (__x == __x->_M_parent->_M_left)
>+	  __x->_M_parent->_M_left = __y;
>+	else
>+	  __x->_M_parent->_M_right = __y;
>+	__y->_M_left = __x;
>+	__x->_M_parent = __y;
>+      }
>+
>+      static void
>+      _Rotate_right(_Base_ptr __x, _Base_ptr& __root)
>+      {
>+	const _Base_ptr __y = __x->_M_left;
>+
>+	__x->_M_left = __y->_M_right;
>+	if (__y->_M_right)
>+	  __y->_M_right->_M_parent = __x;
>+	__y->_M_parent = __x->_M_parent;
>+
>+	if (__x == __root)
>+	  __root = __y;
>+	else if (__x == __x->_M_parent->_M_right)
>+	  __x->_M_parent->_M_right = __y;
>+	else
>+	  __x->_M_parent->_M_left = __y;
>+	__y->_M_right = __x;
>+	__x->_M_parent = __y;
>+      }
>+
>+      static void
>+      _S_insert_and_rebalance(const bool __insert_left,
>+			      _Base_ptr __x, _Base_ptr __p,
>+			      _Node_base& __header)
>+      {
>+	_Base_ptr& __root = __header._M_parent;
>+
>+	// Initialize fields in new node to insert.
>+	__x->_M_parent = __p;
>+	__x->_M_left = __x->_M_right = nullptr;
>+	__x->_M_color = _S_red;
>+
>+	// Insert.
>+	// Make new node child of parent and maintain root, leftmost and
>+	// rightmost nodes.
>+	// N.B. First node is always inserted left.
>+	if (__insert_left)
>+	  {
>+	    __p->_M_left = __x; // also makes leftmost = __x when __p == &__header
>+
>+	    if (std::__to_address(__p) == std::addressof(__header))
>+	      {
>+		__header._M_parent = __x;
>+		__header._M_right = __x;
>+	      }
>+	    else if (__p == __header._M_left)
>+	      __header._M_left = __x; // maintain leftmost pointing to min node
>+	  }
>+	else
>+	  {
>+	    __p->_M_right = __x;
>+
>+	    if (__p == __header._M_right)
>+	      __header._M_right = __x; // maintain rightmost pointing to max node
>+	  }
>+	// Rebalance.
>+	while (__x != __root
>+	       && __x->_M_parent->_M_color == _S_red)
>+	  {
>+	    const _Base_ptr __xpp = __x->_M_parent->_M_parent;
>+
>+	    if (__x->_M_parent == __xpp->_M_left)
>+	      {
>+		const _Base_ptr __y = __xpp->_M_right;
>+		if (__y && __y->_M_color == _S_red)
>+		  {
>+		    __x->_M_parent->_M_color = _S_black;
>+		    __y->_M_color = _S_black;
>+		    __xpp->_M_color = _S_red;
>+		    __x = __xpp;
>+		  }
>+		else
>+		  {
>+		    if (__x == __x->_M_parent->_M_right)
>+		      {
>+			__x = __x->_M_parent;
>+			_Rotate_left(__x, __root);
>+		      }
>+		    __x->_M_parent->_M_color = _S_black;
>+		    __xpp->_M_color = _S_red;
>+		    _Rotate_right(__xpp, __root);
>+		  }
>+	      }
>+	    else
>+	      {
>+		const _Base_ptr __y = __xpp->_M_left;
>+		if (__y && __y->_M_color == _S_red)
>+		  {
>+		    __x->_M_parent->_M_color = _S_black;
>+		    __y->_M_color = _S_black;
>+		    __xpp->_M_color = _S_red;
>+		    __x = __xpp;
>+		  }
>+		else
>+		  {
>+		    if (__x == __x->_M_parent->_M_left)
>+		      {
>+			__x = __x->_M_parent;
>+			_Rotate_right(__x, __root);
>+		      }
>+		    __x->_M_parent->_M_color = _S_black;
>+		    __xpp->_M_color = _S_red;
>+		    _Rotate_left(__xpp, __root);
>+		  }
>+	      }
>+	  }
>+	__root->_M_color = _S_black;
>+      }
>+
>+      static _Base_ptr
>+      _S_rebalance_for_erase(_Base_ptr __z, _Node_base& __header)
>+      {
>+	_Base_ptr& __root = __header._M_parent;
>+	_Base_ptr& __leftmost = __header._M_left;
>+	_Base_ptr& __rightmost = __header._M_right;
>+	_Base_ptr __y = __z;
>+	_Base_ptr __x{};
>+	_Base_ptr __x_parent{};
>+
>+	if (!__y->_M_left)     // __z has at most one non-null child. y == z.
>+	  __x = __y->_M_right;     // __x might be null.
>+	else
>+	  if (!__y->_M_right)  // __z has exactly one non-null child. y == z.
>+	    __x = __y->_M_left;    // __x is not null.
>+	  else
>+	    {
>+	      // __z has two non-null children.  Set __y to
>+	      __y = __y->_M_right;   //   __z's successor.  __x might be null.
>+	      while (__y->_M_left)
>+		__y = __y->_M_left;
>+	      __x = __y->_M_right;
>+	    }
>+	if (__y != __z)
>+	  {
>+	    // relink y in place of z.  y is z's successor
>+	    __z->_M_left->_M_parent = __y;
>+	    __y->_M_left = __z->_M_left;
>+	    if (__y != __z->_M_right)
>+	      {
>+		__x_parent = __y->_M_parent;
>+		if (__x)
>+		  __x->_M_parent = __y->_M_parent;
>+		__y->_M_parent->_M_left = __x;   // __y must be a child of _M_left
>+		__y->_M_right = __z->_M_right;
>+		__z->_M_right->_M_parent = __y;
>+	      }
>+	    else
>+	      __x_parent = __y;
>+	    if (__root == __z)
>+	      __root = __y;
>+	    else if (__z->_M_parent->_M_left == __z)
>+	      __z->_M_parent->_M_left = __y;
>+	    else
>+	      __z->_M_parent->_M_right = __y;
>+	    __y->_M_parent = __z->_M_parent;
>+	    std::swap(__y->_M_color, __z->_M_color);
>+	    __y = __z;
>+	    // __y now points to node to be actually deleted
>+	  }
>+	else
>+	  {                        // __y == __z
>+	    __x_parent = __y->_M_parent;
>+	    if (__x)
>+	      __x->_M_parent = __y->_M_parent;
>+	    if (__root == __z)
>+	      __root = __x;
>+	    else
>+	      if (__z->_M_parent->_M_left == __z)
>+		__z->_M_parent->_M_left = __x;
>+	      else
>+		__z->_M_parent->_M_right = __x;
>+	    if (__leftmost == __z)
>+	      {
>+		if (!__z->_M_right)        // __z->_M_left must be null also
>+		  __leftmost = __z->_M_parent;
>+		// makes __leftmost == _M_header if __z == __root
>+		else
>+		  __leftmost = _Node_base::_S_minimum(__x);
>+	      }
>+	    if (__rightmost == __z)
>+	      {
>+		if (__z->_M_left == 0)         // __z->_M_right must be null also
>+		  __rightmost = __z->_M_parent;
>+		// makes __rightmost == _M_header if __z == __root
>+		else                      // __x == __z->_M_left
>+		  __rightmost = _Node_base::_S_maximum(__x);
>+	      }
>+	  }
>+	if (__y->_M_color != _S_red)
>+	  {
>+	    while (__x != __root && (__x == 0 || __x->_M_color == _S_black))
>+	      if (__x == __x_parent->_M_left)
>+		{
>+		  _Base_ptr __w = __x_parent->_M_right;
>+		  if (__w->_M_color == _S_red)
>+		    {
>+		      __w->_M_color = _S_black;
>+		      __x_parent->_M_color = _S_red;
>+		      _Rotate_left(__x_parent, __root);
>+		      __w = __x_parent->_M_right;
>+		    }
>+		  if ((!__w->_M_left || __w->_M_left->_M_color == _S_black) &&
>+		      (!__w->_M_right || __w->_M_right->_M_color == _S_black))
>+		    {
>+		      __w->_M_color = _S_red;
>+		      __x = __x_parent;
>+		      __x_parent = __x_parent->_M_parent;
>+		    }
>+		  else
>+		    {
>+		      if (!__w->_M_right || __w->_M_right->_M_color == _S_black)
>+			{
>+			  __w->_M_left->_M_color = _S_black;
>+			  __w->_M_color = _S_red;
>+			  _Rotate_right(__w, __root);
>+			  __w = __x_parent->_M_right;
>+			}
>+		      __w->_M_color = __x_parent->_M_color;
>+		      __x_parent->_M_color = _S_black;
>+		      if (__w->_M_right)
>+			__w->_M_right->_M_color = _S_black;
>+		      _Rotate_left(__x_parent, __root);
>+		      break;
>+		    }
>+		}
>+	      else
>+		{
>+		  // same as above, with _M_right <-> _M_left.
>+		  _Base_ptr __w = __x_parent->_M_left;
>+		  if (__w->_M_color == _S_red)
>+		    {
>+		      __w->_M_color = _S_black;
>+		      __x_parent->_M_color = _S_red;
>+		      _Rotate_right(__x_parent, __root);
>+		      __w = __x_parent->_M_left;
>+		    }
>+		  if ((!__w->_M_right || __w->_M_right->_M_color == _S_black) &&
>+		      (!__w->_M_left || __w->_M_left->_M_color == _S_black))
>+		    {
>+		      __w->_M_color = _S_red;
>+		      __x = __x_parent;
>+		      __x_parent = __x_parent->_M_parent;
>+		    }
>+		  else
>+		    {
>+		      if (!__w->_M_left || __w->_M_left->_M_color == _S_black)
>+			{
>+			  __w->_M_right->_M_color = _S_black;
>+			  __w->_M_color = _S_red;
>+			  _Rotate_left(__w, __root);
>+			  __w = __x_parent->_M_left;
>+			}
>+		      __w->_M_color = __x_parent->_M_color;
>+		      __x_parent->_M_color = _S_black;
>+		      if (__w->_M_left)
>+			__w->_M_left->_M_color = _S_black;
>+		      _Rotate_right(__x_parent, __root);
>+		      break;
>+		    }
>+		}
>+	    if (__x)
>+	      __x->_M_color = _S_black;
>+	  }
>+
>+	return __y;
>+      }
>+    };
>+#endif
>+} // namespace __rb_tree
>+
> #if __cplusplus > 201402L
>   template<typename _Tree1, typename _Cmp2>
>     struct _Rb_tree_merge_helper { };
>@@ -425,15 +1025,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     class _Rb_tree
>     {
>       typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
>-	rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
>+	rebind<_Val>::other _Val_alloc_type;
>+
>+      typedef __gnu_cxx::__alloc_traits<_Val_alloc_type> _Val_alloc_traits;
>+      typedef typename _Val_alloc_traits::pointer _ValPtr;
>+      typedef __rb_tree::_Node_traits<_Val, _ValPtr> _Node_traits;
>+
>+      typedef typename _Node_traits::_Node_base		_Node_base;
>+      typedef typename _Node_traits::_Node		_Node;
>+
>+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
>+	rebind<_Node>::other _Node_allocator;
>
>-      typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
>+      typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Node_alloc_traits;
>
>     protected:
>-      typedef _Rb_tree_node_base* 		_Base_ptr;
>-      typedef const _Rb_tree_node_base* 	_Const_Base_ptr;
>-      typedef _Rb_tree_node<_Val>* 		_Link_type;
>-      typedef const _Rb_tree_node<_Val>*	_Const_Link_type;
>+      typedef typename _Node_base::_Base_ptr 		_Base_ptr;
>+      typedef typename _Node_alloc_traits::pointer	_Node_ptr;
>
>     private:
>       // Functor recycling a pool of nodes and using allocation once the pool
>@@ -445,13 +1053,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	{
> 	  if (_M_root)
> 	    {
>-	      _M_root->_M_parent = 0;
>+	      _M_root->_M_parent = _Base_ptr();
>
> 	      if (_M_nodes->_M_left)
> 		_M_nodes = _M_nodes->_M_left;
> 	    }
> 	  else
>-	    _M_nodes = 0;
>+	    _M_nodes = _Base_ptr();
> 	}
>
> #if __cplusplus >= 201103L
>@@ -459,13 +1067,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> #endif
>
> 	~_Reuse_or_alloc_node()
>-	{ _M_t._M_erase(static_cast<_Link_type>(_M_root)); }
>+	{ _M_t._M_erase(_M_root); }
>
> 	template<typename _Arg>
>-	  _Link_type
>+	  _Base_ptr

Changing this from a node pointer to a base pointer seems to weaken
the API. We know it returns a pointer to a node, so can it return the
correct type?

> 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg)
> 	  {
>-	    _Link_type __node = static_cast<_Link_type>(_M_extract());
>+	    _Base_ptr __node = _M_extract();
> 	    if (__node)
> 	      {
> 		_M_t._M_destroy_node(__node);
>@@ -489,7 +1097,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	    {
> 	      if (_M_nodes->_M_right == __node)
> 		{
>-		  _M_nodes->_M_right = 0;
>+		  _M_nodes->_M_right = _Base_ptr();
>
> 		  if (_M_nodes->_M_left)
> 		    {
>@@ -503,10 +1111,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 		    }
> 		}
> 	      else // __node is on the left.
>-		_M_nodes->_M_left = 0;
>+		_M_nodes->_M_left = _Base_ptr();
> 	    }
> 	  else
>-	    _M_root = 0;
>+	    _M_root = _Base_ptr();
>
> 	  return __node;
> 	}
>@@ -524,9 +1132,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	: _M_t(__t) { }
>
> 	template<typename _Arg>
>-	  _Link_type
>+	  _Base_ptr

Same comment here.

> 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg) const
>-	  { return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg)); }
>+	  {
>+	    return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg));
>+	  }
>
>       private:
> 	_Rb_tree& _M_t;
>@@ -556,18 +1166,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       { return allocator_type(_M_get_Node_allocator()); }
>
>     protected:
>-      _Link_type
>+      _Node_ptr
>       _M_get_node()
>-      { return _Alloc_traits::allocate(_M_get_Node_allocator(), 1); }
>+      { return _Node_alloc_traits::allocate(_M_get_Node_allocator(), 1); }
>
>       void
>-      _M_put_node(_Link_type __p) _GLIBCXX_NOEXCEPT
>-      { _Alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1); }
>+      _M_put_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
>+      {
>+	_Node_alloc_traits::deallocate
>+	  (_M_get_Node_allocator(), __p->template _M_node_ptr<_Node_ptr>(), 1);
>+      }
>
> #if __cplusplus < 201103L
>       void
>-      _M_construct_node(_Link_type __node, const value_type& __x)
>+      _M_construct_node(_Base_ptr __p, const value_type& __x)

And can this be left as _Node_ptr instead of changed to _Base_ptr?
Then it wouldn't need the static_cast, and the callers wouldn't need
to use _M_base_ptr().

Logically, it has to be a _Node_ptr or the cast isn't valid anyway.

There seem to be several places like this where we're giving up
valuable type information by using _base_ptr instead of _Node_ptr.

Is that really necessary? If we have a _Node_ptr we can always get a
_Base_ptr easily by calling _M_base_ptr().

Is it a constness thing, because _M_begin() const was changed to
return _Base_ptr instead of _Const_link_ptr?


>       {
>+	_Node_ptr __node = static_cast<_Node_ptr>(__p);
> 	__try
> 	  { get_allocator().construct(__node->_M_valptr(), __x); }
> 	__catch(...)
>@@ -577,79 +1191,83 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	  }
>       }
>
>-      _Link_type
>+      _Base_ptr
>       _M_create_node(const value_type& __x)
>       {
>-	_Link_type __tmp = _M_get_node();
>+	_Base_ptr __tmp = _M_get_node()->_M_base_ptr();
> 	_M_construct_node(__tmp, __x);
> 	return __tmp;
>       }
> #else
>       template<typename... _Args>
> 	void
>-	_M_construct_node(_Link_type __node, _Args&&... __args)
>+	_M_construct_node(_Base_ptr __p, _Args&&... __args)
> 	{
>+	  _Node& __node = static_cast<_Node&>(*__p);
> 	  __try
> 	    {
>-	      ::new(__node) _Rb_tree_node<_Val>;
>-	      _Alloc_traits::construct(_M_get_Node_allocator(),
>-				       __node->_M_valptr(),
>-				       std::forward<_Args>(__args)...);
>+	      ::new(std::__addressof(__node)) _Node;
>+	      _Node_alloc_traits::construct(_M_get_Node_allocator(),
>+					    __node._M_valptr(),
>+					    std::forward<_Args>(__args)...);
> 	    }
> 	  __catch(...)
> 	    {
>-	      __node->~_Rb_tree_node<_Val>();
>-	      _M_put_node(__node);
>+	      __node.~_Node();
>+	      _M_put_node(__p);
> 	      __throw_exception_again;
> 	    }
> 	}
>
>       template<typename... _Args>
>-	_Link_type
>+	_Base_ptr
> 	_M_create_node(_Args&&... __args)
> 	{
>-	  _Link_type __tmp = _M_get_node();
>+	  _Base_ptr __tmp = _M_get_node()->_M_base_ptr();
> 	  _M_construct_node(__tmp, std::forward<_Args>(__args)...);
> 	  return __tmp;
> 	}
> #endif
>
>       void
>-      _M_destroy_node(_Link_type __p) _GLIBCXX_NOEXCEPT
>+      _M_destroy_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
>       {
> #if __cplusplus < 201103L
>-	get_allocator().destroy(__p->_M_valptr());
>+	get_allocator().destroy(static_cast<_Node_ptr>(__p)->_M_valptr());
> #else
>-	_Alloc_traits::destroy(_M_get_Node_allocator(), __p->_M_valptr());
>-	__p->~_Rb_tree_node<_Val>();
>+	_Node& __node = static_cast<_Node&>(*__p);
>+	_Node_alloc_traits::destroy(_M_get_Node_allocator(),
>+				    __node._M_valptr());
>+	__node.~_Node();
> #endif
>       }
>
>       void
>-      _M_drop_node(_Link_type __p) _GLIBCXX_NOEXCEPT
>+      _M_drop_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
>       {
> 	_M_destroy_node(__p);
> 	_M_put_node(__p);
>       }
>
>       template<bool _MoveValue, typename _NodeGen>
>-	_Link_type
>-	_M_clone_node(_Link_type __x, _NodeGen& __node_gen)
>+	_Base_ptr
>+	_M_clone_node(_Base_ptr __x, _NodeGen& __node_gen)
> 	{
> #if __cplusplus >= 201103L
> 	  using _Vp = __conditional_t<_MoveValue,
> 				      value_type&&,
> 				      const value_type&>;
> #endif
>-	  _Link_type __tmp
>-	    = __node_gen(_GLIBCXX_FORWARD(_Vp, *__x->_M_valptr()));
>+	  _Base_ptr __tmp = __node_gen
>+	    (_GLIBCXX_FORWARD(_Vp, *static_cast<_Node&>(*__x)._M_valptr()));
> 	  __tmp->_M_color = __x->_M_color;
>-	  __tmp->_M_left = 0;
>-	  __tmp->_M_right = 0;
>+	  __tmp->_M_left = __tmp->_M_right = _Base_ptr();
> 	  return __tmp;
> 	}
>
>     protected:
>+      typedef typename _Node_traits::_Header_t		_Header_t;
>+
> #if _GLIBCXX_INLINE_VERSION
>       template<typename _Key_compare>
> #else
>@@ -660,7 +1278,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	struct _Rb_tree_impl
> 	: public _Node_allocator
> 	, public _Rb_tree_key_compare<_Key_compare>
>-	, public _Rb_tree_header
>+	, public _Header_t
> 	{
> 	  typedef _Rb_tree_key_compare<_Key_compare> _Base_key_compare;
>
>@@ -672,9 +1290,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	  { }
>
> 	  _Rb_tree_impl(const _Rb_tree_impl& __x)
>-	  : _Node_allocator(_Alloc_traits::_S_select_on_copy(__x))
>+	  : _Node_allocator(_Node_alloc_traits::_S_select_on_copy(__x))
> 	  , _Base_key_compare(__x._M_key_compare)
>-	  , _Rb_tree_header()
>+	  , _Header_t()
> 	  { }
>
> #if __cplusplus < 201103L
>@@ -694,7 +1312,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	  _Rb_tree_impl(_Rb_tree_impl&& __x, _Node_allocator&& __a)
> 	  : _Node_allocator(std::move(__a)),
> 	    _Base_key_compare(std::move(__x)),
>-	    _Rb_tree_header(std::move(__x))
>+	    _Header_t(std::move(__x))
> 	  { }
>
> 	  _Rb_tree_impl(const _Key_compare& __comp, _Node_allocator&& __a)
>@@ -710,7 +1328,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _M_root() _GLIBCXX_NOEXCEPT
>       { return this->_M_impl._M_header._M_parent; }
>
>-      _Const_Base_ptr
>+      _Base_ptr
>       _M_root() const _GLIBCXX_NOEXCEPT
>       { return this->_M_impl._M_header._M_parent; }
>
>@@ -718,7 +1336,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _M_leftmost() _GLIBCXX_NOEXCEPT
>       { return this->_M_impl._M_header._M_left; }
>
>-      _Const_Base_ptr
>+      _Base_ptr
>       _M_leftmost() const _GLIBCXX_NOEXCEPT
>       { return this->_M_impl._M_header._M_left; }
>
>@@ -726,35 +1344,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _M_rightmost() _GLIBCXX_NOEXCEPT
>       { return this->_M_impl._M_header._M_right; }
>
>-      _Const_Base_ptr
>+      _Base_ptr
>       _M_rightmost() const _GLIBCXX_NOEXCEPT
>       { return this->_M_impl._M_header._M_right; }
>
>-      _Link_type
>-      _M_mbegin() const _GLIBCXX_NOEXCEPT
>-      { return static_cast<_Link_type>(this->_M_impl._M_header._M_parent); }
>-
>-      _Link_type
>-      _M_begin() _GLIBCXX_NOEXCEPT
>-      { return _M_mbegin(); }
>-
>-      _Const_Link_type
>+      _Base_ptr
>       _M_begin() const _GLIBCXX_NOEXCEPT
>-      {
>-	return static_cast<_Const_Link_type>
>-	  (this->_M_impl._M_header._M_parent);
>-      }
>+      { return this->_M_impl._M_header._M_parent; }
>
>       _Base_ptr
>-      _M_end() _GLIBCXX_NOEXCEPT
>-      { return &this->_M_impl._M_header; }
>-
>-      _Const_Base_ptr
>       _M_end() const _GLIBCXX_NOEXCEPT
>-      { return &this->_M_impl._M_header; }
>+      { return this->_M_impl._M_header._M_base_ptr(); }
>
>       static const _Key&
>-      _S_key(_Const_Link_type __x)
>+      _S_key(_Base_ptr __x)
>       {
> #if __cplusplus >= 201103L
> 	// If we're asking for the key we're presumably using the comparison
>@@ -772,48 +1375,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> # endif // C++17
> #endif // C++11
>
>-	return _KeyOfValue()(*__x->_M_valptr());
>+	return _KeyOfValue()(*static_cast<const _Node&>(*__x)._M_valptr());
>       }
>
>-      static _Link_type
>-      _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>-      { return static_cast<_Link_type>(__x->_M_left); }
>-
>-      static _Const_Link_type
>-      _S_left(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>-      { return static_cast<_Const_Link_type>(__x->_M_left); }
>-
>-      static _Link_type
>-      _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>-      { return static_cast<_Link_type>(__x->_M_right); }
>-
>-      static _Const_Link_type
>-      _S_right(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>-      { return static_cast<_Const_Link_type>(__x->_M_right); }
>-
>-      static const _Key&
>-      _S_key(_Const_Base_ptr __x)
>-      { return _S_key(static_cast<_Const_Link_type>(__x)); }
>-
>       static _Base_ptr
>-      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>-      { return _Rb_tree_node_base::_S_minimum(__x); }
>-
>-      static _Const_Base_ptr
>-      _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>-      { return _Rb_tree_node_base::_S_minimum(__x); }
>+      _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>+      { return __x->_M_left; }
>
>       static _Base_ptr
>-      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>-      { return _Rb_tree_node_base::_S_maximum(__x); }
>-
>-      static _Const_Base_ptr
>-      _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
>-      { return _Rb_tree_node_base::_S_maximum(__x); }
>+      _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
>+      { return __x->_M_right; }
>
>     public:
>-      typedef _Rb_tree_iterator<value_type>       iterator;
>-      typedef _Rb_tree_const_iterator<value_type> const_iterator;
>+      typedef typename _Node_traits::_Iterator		iterator;
>+      typedef typename _Node_traits::_Const_iterator	const_iterator;
>
>       typedef std::reverse_iterator<iterator>       reverse_iterator;
>       typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
>@@ -846,7 +1421,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	_M_insert_(_Base_ptr __x, _Base_ptr __y, _Arg&& __v, _NodeGen&);
>
>       iterator
>-      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Link_type __z);
>+      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Base_ptr __z);
>
>       template<typename _Arg>
> 	iterator
>@@ -857,10 +1432,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	_M_insert_equal_lower(_Arg&& __x);
>
>       iterator
>-      _M_insert_lower_node(_Base_ptr __p, _Link_type __z);
>+      _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z);
>
>       iterator
>-      _M_insert_equal_lower_node(_Link_type __z);
>+      _M_insert_equal_lower_node(_Base_ptr __z);
> #else
>       template<typename _NodeGen>
> 	iterator
>@@ -879,22 +1454,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       enum { __as_lvalue, __as_rvalue };
>
>       template<bool _MoveValues, typename _NodeGen>
>-	_Link_type
>-	_M_copy(_Link_type, _Base_ptr, _NodeGen&);
>+	_Base_ptr
>+	_M_copy(_Base_ptr, _Base_ptr, _NodeGen&);
>
>       template<bool _MoveValues, typename _NodeGen>
>-	_Link_type
>+	_Base_ptr
> 	_M_copy(const _Rb_tree& __x, _NodeGen& __gen)
> 	{
>-	  _Link_type __root =
>-	    _M_copy<_MoveValues>(__x._M_mbegin(), _M_end(), __gen);
>-	  _M_leftmost() = _S_minimum(__root);
>-	  _M_rightmost() = _S_maximum(__root);
>+	  _Base_ptr __root =
>+	    _M_copy<_MoveValues>(__x._M_begin(), _M_end(), __gen);
>+	  _M_leftmost() = _Node_base::_S_minimum(__root);
>+	  _M_rightmost() = _Node_base::_S_maximum(__root);
> 	  _M_impl._M_node_count = __x._M_impl._M_node_count;
> 	  return __root;
> 	}
>
>-      _Link_type
>+      _Base_ptr
>       _M_copy(const _Rb_tree& __x)
>       {
> 	_Alloc_node __an(*this);
>@@ -902,22 +1477,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       }
>
>       void
>-      _M_erase(_Link_type __x);
>+      _M_erase(_Base_ptr __x);
>
>-      iterator
>-      _M_lower_bound(_Link_type __x, _Base_ptr __y,
>-		     const _Key& __k);
>-
>-      const_iterator
>-      _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
>+      _Base_ptr
>+      _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
> 		     const _Key& __k) const;
>
>-      iterator
>-      _M_upper_bound(_Link_type __x, _Base_ptr __y,
>-		     const _Key& __k);
>-
>-      const_iterator
>-      _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
>+      _Base_ptr
>+      _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
> 		     const _Key& __k) const;
>
>     public:
>@@ -935,7 +1502,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _Rb_tree(const _Rb_tree& __x)
>       : _M_impl(__x._M_impl)
>       {
>-	if (__x._M_root() != 0)
>+	if (__x._M_root())
> 	  _M_root() = _M_copy(__x);
>       }
>
>@@ -947,7 +1514,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _Rb_tree(const _Rb_tree& __x, const allocator_type& __a)
>       : _M_impl(__x._M_impl._M_key_compare, _Node_allocator(__a))
>       {
>-	if (__x._M_root() != nullptr)
>+	if (__x._M_root())
> 	  _M_root() = _M_copy(__x);
>       }
>
>@@ -966,7 +1533,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a, false_type)
>       : _M_impl(__x._M_impl._M_key_compare, std::move(__a))
>       {
>-	if (__x._M_root() != nullptr)
>+	if (__x._M_root())
> 	  _M_move_data(__x, false_type{});
>       }
>
>@@ -974,9 +1541,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a)
>       noexcept( noexcept(
> 	_Rb_tree(std::declval<_Rb_tree&&>(), std::declval<_Node_allocator&&>(),
>-		 std::declval<typename _Alloc_traits::is_always_equal>())) )
>+		 std::declval<typename _Node_alloc_traits::is_always_equal>())) )
>       : _Rb_tree(std::move(__x), std::move(__a),
>-		 typename _Alloc_traits::is_always_equal{})
>+		 typename _Node_alloc_traits::is_always_equal{})
>       { }
> #endif
>
>@@ -1001,11 +1568,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>       iterator
>       end() _GLIBCXX_NOEXCEPT
>-      { return iterator(&this->_M_impl._M_header); }
>+      { return iterator(_M_end()); }
>
>       const_iterator
>       end() const _GLIBCXX_NOEXCEPT
>-      { return const_iterator(&this->_M_impl._M_header); }
>+      { return const_iterator(_M_end()); }
>
>       reverse_iterator
>       rbegin() _GLIBCXX_NOEXCEPT
>@@ -1033,7 +1600,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>       size_type
>       max_size() const _GLIBCXX_NOEXCEPT
>-      { return _Alloc_traits::max_size(_M_get_Node_allocator()); }
>+      { return _Node_alloc_traits::max_size(_M_get_Node_allocator()); }
>
>       void
>       swap(_Rb_tree& __t)
>@@ -1194,7 +1761,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	const_iterator __result = __position;
> 	++__result;
> 	_M_erase_aux(__position);
>-	return __result._M_const_cast();
>+	return iterator(__result._M_node);
>       }
>
>       // LWG 2059.
>@@ -1235,7 +1802,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       erase(const_iterator __first, const_iterator __last)
>       {
> 	_M_erase_aux(__first, __last);
>-	return __last._M_const_cast();
>+	return iterator(__last._M_node);
>       }
> #else
>       void
>@@ -1266,19 +1833,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>       iterator
>       lower_bound(const key_type& __k)
>-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
>+      { return iterator(_M_lower_bound(_M_begin(), _M_end(), __k)); }
>
>       const_iterator
>       lower_bound(const key_type& __k) const
>-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
>+      {
>+	return const_iterator
>+	  (_M_lower_bound(_M_begin(), _M_end(), __k));
>+      }
>
>       iterator
>       upper_bound(const key_type& __k)
>-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
>+      { return iterator(_M_upper_bound(_M_begin(), _M_end(), __k)); }
>
>       const_iterator
>       upper_bound(const key_type& __k) const
>-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
>+      {
>+	return const_iterator
>+	  (_M_upper_bound(_M_begin(), _M_end(), __k));
>+      }
>
>       pair<iterator, iterator>
>       equal_range(const key_type& __k);
>@@ -1293,7 +1866,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	_M_find_tr(const _Kt& __k)
> 	{
> 	  const _Rb_tree* __const_this = this;
>-	  return __const_this->_M_find_tr(__k)._M_const_cast();
>+	  return iterator(__const_this->_M_find_tr(__k)._M_node);
> 	}
>
>       template<typename _Kt,
>@@ -1301,7 +1874,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	const_iterator
> 	_M_find_tr(const _Kt& __k) const
> 	{
>-	  auto __j = _M_lower_bound_tr(__k);
>+	  const_iterator __j(_M_lower_bound_tr(__k));
> 	  if (__j != end() && _M_impl._M_key_compare(__k, _S_key(__j._M_node)))
> 	    __j = end();
> 	  return __j;
>@@ -1318,21 +1891,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>       template<typename _Kt,
> 	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
>-	iterator
>-	_M_lower_bound_tr(const _Kt& __k)
>-	{
>-	  const _Rb_tree* __const_this = this;
>-	  return __const_this->_M_lower_bound_tr(__k)._M_const_cast();
>-	}
>-
>-      template<typename _Kt,
>-	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
>-	const_iterator
>+	_Base_ptr
> 	_M_lower_bound_tr(const _Kt& __k) const
> 	{
> 	  auto __x = _M_begin();
> 	  auto __y = _M_end();
>-	  while (__x != 0)
>+	  while (__x)
> 	    if (!_M_impl._M_key_compare(_S_key(__x), __k))
> 	      {
> 		__y = __x;
>@@ -1340,26 +1904,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	      }
> 	    else
> 	      __x = _S_right(__x);
>-	  return const_iterator(__y);
>+	  return __y;
> 	}
>
>       template<typename _Kt,
> 	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
>-	iterator
>-	_M_upper_bound_tr(const _Kt& __k)
>-	{
>-	  const _Rb_tree* __const_this = this;
>-	  return __const_this->_M_upper_bound_tr(__k)._M_const_cast();
>-	}
>-
>-      template<typename _Kt,
>-	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
>-	const_iterator
>+	_Base_ptr
> 	_M_upper_bound_tr(const _Kt& __k) const
> 	{
> 	  auto __x = _M_begin();
> 	  auto __y = _M_end();
>-	  while (__x != 0)
>+	  while (__x)
> 	    if (_M_impl._M_key_compare(__k, _S_key(__x)))
> 	      {
> 		__y = __x;
>@@ -1367,7 +1922,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	      }
> 	    else
> 	      __x = _S_right(__x);
>-	  return const_iterator(__y);
>+	  return __y;
> 	}
>
>       template<typename _Kt,
>@@ -1377,7 +1932,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	{
> 	  const _Rb_tree* __const_this = this;
> 	  auto __ret = __const_this->_M_equal_range_tr(__k);
>-	  return { __ret.first._M_const_cast(), __ret.second._M_const_cast() };
>+	  return
>+	    { iterator(__ret.first._M_node), iterator(__ret.second._M_node) };
> 	}
>
>       template<typename _Kt,
>@@ -1385,7 +1941,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	pair<const_iterator, const_iterator>
> 	_M_equal_range_tr(const _Kt& __k) const
> 	{
>-	  auto __low = _M_lower_bound_tr(__k);
>+	  const_iterator __low(_M_lower_bound_tr(__k));
> 	  auto __high = __low;
> 	  auto& __cmp = _M_impl._M_key_compare;
> 	  while (__high != end() && !__cmp(__k, _S_key(__high._M_node)))
>@@ -1401,7 +1957,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> #if __cplusplus >= 201103L
>       _Rb_tree&
>       operator=(_Rb_tree&&)
>-      noexcept(_Alloc_traits::_S_nothrow_move()
>+      noexcept(_Node_alloc_traits::_S_nothrow_move()
> 	       && is_nothrow_move_assignable<_Compare>::value);
>
>       template<typename _Iterator>
>@@ -1449,8 +2005,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	    auto __res = _M_get_insert_unique_pos(__nh._M_key());
> 	    if (__res.second)
> 	      {
>-		__ret.position
>-		  = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
>+		__ret.position = _M_insert_node(__res.first, __res.second,
>+						__nh._M_ptr->_M_base_ptr());
> 		__nh.release();
> 		__ret.inserted = true;
> 	      }
>@@ -1476,9 +2032,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	    __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
> 	    auto __res = _M_get_insert_equal_pos(__nh._M_key());
> 	    if (__res.second)
>-	      __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
>+	      __ret = _M_insert_node(__res.first, __res.second,
>+				     __nh._M_ptr->_M_base_ptr());
> 	    else
>-	      __ret = _M_insert_equal_lower_node(__nh._M_ptr);
>+	      __ret = _M_insert_equal_lower_node(__nh._M_ptr->_M_base_ptr());
> 	    __nh.release();
> 	  }
> 	return __ret;
>@@ -1497,7 +2054,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	    auto __res = _M_get_insert_hint_unique_pos(__hint, __nh._M_key());
> 	    if (__res.second)
> 	      {
>-		__ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
>+		__ret = _M_insert_node(__res.first, __res.second,
>+				       __nh._M_ptr->_M_base_ptr());
> 		__nh.release();
> 	      }
> 	    else
>@@ -1518,9 +2076,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	    __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
> 	    auto __res = _M_get_insert_hint_equal_pos(__hint, __nh._M_key());
> 	    if (__res.second)
>-	      __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
>+	      __ret = _M_insert_node(__res.first, __res.second,
>+				     __nh._M_ptr->_M_base_ptr());
> 	    else
>-	      __ret = _M_insert_equal_lower_node(__nh._M_ptr);
>+	      __ret = _M_insert_equal_lower_node(__nh._M_ptr->_M_base_ptr());
> 	    __nh.release();
> 	  }
> 	return __ret;
>@@ -1530,10 +2089,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       node_type
>       extract(const_iterator __pos)
>       {
>-	auto __ptr = _Rb_tree_rebalance_for_erase(
>-	    __pos._M_const_cast()._M_node, _M_impl._M_header);
>+	auto __ptr = _Node_traits::_S_rebalance_for_erase
>+	  (__pos._M_node, _M_impl._M_header);
> 	--_M_impl._M_node_count;
>-	return { static_cast<_Link_type>(__ptr), _M_get_Node_allocator() };
>+	return
>+	  { __ptr->template _M_node_ptr<_Node_ptr>(), _M_get_Node_allocator() };
>       }
>
>       /// Extract a node.
>@@ -1567,11 +2127,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	      if (__res.second)
> 		{
> 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
>-		  auto __ptr = _Rb_tree_rebalance_for_erase(
>-		      __pos._M_node, __src_impl._M_header);
>+		  auto __ptr = _Node_traits::_S_rebalance_for_erase
>+		    (__pos._M_node, __src_impl._M_header);
> 		  --__src_impl._M_node_count;
>-		  _M_insert_node(__res.first, __res.second,
>-				 static_cast<_Link_type>(__ptr));
>+		  _M_insert_node(__res.first, __res.second, __ptr);
> 		}
> 	    }
> 	}
>@@ -1589,11 +2148,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	      if (__res.second)
> 		{
> 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
>-		  auto __ptr = _Rb_tree_rebalance_for_erase(
>-		      __pos._M_node, __src_impl._M_header);
>+		  auto __ptr = _Node_traits::_S_rebalance_for_erase
>+		    (__pos._M_node, __src_impl._M_header);
> 		  --__src_impl._M_node_count;
>-		  _M_insert_node(__res.first, __res.second,
>-				 static_cast<_Link_type>(__ptr));
>+		  _M_insert_node(__res.first, __res.second, __ptr);
> 		}
> 	    }
> 	}
>@@ -1666,7 +2224,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	}
>
> 	_Rb_tree& _M_t;
>-	_Link_type _M_node;
>+	_Base_ptr _M_node;
>       };
> #endif // C++11
>     };
>@@ -1704,7 +2262,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _M_move_assign(_Rb_tree& __x, true_type)
>     {
>       clear();
>-      if (__x._M_root() != nullptr)
>+      if (__x._M_root())
> 	_M_move_data(__x, true_type());
>       std::__alloc_on_move(_M_get_Node_allocator(),
> 			   __x._M_get_Node_allocator());
>@@ -1723,7 +2281,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       // structure.
>       _Reuse_or_alloc_node __roan(*this);
>       _M_impl._M_reset();
>-      if (__x._M_root() != nullptr)
>+      if (__x._M_root())
> 	{
> 	  _M_root() = _M_copy<__as_rvalue>(__x, __roan);
> 	  __x.clear();
>@@ -1735,11 +2293,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     inline _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>     operator=(_Rb_tree&& __x)
>-    noexcept(_Alloc_traits::_S_nothrow_move()
>+    noexcept(_Node_alloc_traits::_S_nothrow_move()
> 	     && is_nothrow_move_assignable<_Compare>::value)
>     {
>       _M_impl._M_key_compare = std::move(__x._M_impl._M_key_compare);
>-      _M_move_assign(__x, __bool_constant<_Alloc_traits::_S_nothrow_move()>());
>+      _M_move_assign(__x,
>+		     __bool_constant<_Node_alloc_traits::_S_nothrow_move()>());
>       return *this;
>     }
>
>@@ -1780,11 +2339,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	{
> 	  // Note that _Key may be a constant type.
> #if __cplusplus >= 201103L
>-	  if (_Alloc_traits::_S_propagate_on_copy_assign())
>+	  if (_Node_alloc_traits::_S_propagate_on_copy_assign())
> 	    {
> 	      auto& __this_alloc = this->_M_get_Node_allocator();
> 	      auto& __that_alloc = __x._M_get_Node_allocator();
>-	      if (!_Alloc_traits::_S_always_equal()
>+	      if (!_Node_alloc_traits::_S_always_equal()
> 		  && __this_alloc != __that_alloc)
> 		{
> 		  // Replacement allocator cannot free existing storage, we need
>@@ -1798,7 +2357,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	  _Reuse_or_alloc_node __roan(*this);
> 	  _M_impl._M_reset();
> 	  _M_impl._M_key_compare = __x._M_impl._M_key_compare;
>-	  if (__x._M_root() != 0)
>+	  if (__x._M_root())
> 	    _M_root() = _M_copy<__as_lvalue>(__x, __roan);
> 	}
>
>@@ -1822,14 +2381,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> #endif
> 		 _NodeGen& __node_gen)
>       {
>-	bool __insert_left = (__x != 0 || __p == _M_end()
>+	bool __insert_left = (__x || __p == _M_end()
> 			      || _M_impl._M_key_compare(_KeyOfValue()(__v),
> 							_S_key(__p)));
>
>-	_Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
>+	_Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
>
>-	_Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
>-				      this->_M_impl._M_header);
>+	_Node_traits::_S_insert_and_rebalance
>+	  (__insert_left, __z, __p, this->_M_impl._M_header);
> 	++_M_impl._M_node_count;
> 	return iterator(__z);
>       }
>@@ -1851,10 +2410,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 			    || !_M_impl._M_key_compare(_S_key(__p),
> 						       _KeyOfValue()(__v)));
>
>-      _Link_type __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
>-
>-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
>-				    this->_M_impl._M_header);
>+      _Base_ptr __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
>+      _Node_traits::_S_insert_and_rebalance
>+	(__insert_left, __z, __p, this->_M_impl._M_header);
>       ++_M_impl._M_node_count;
>       return iterator(__z);
>     }
>@@ -1872,9 +2430,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _M_insert_equal_lower(const _Val& __v)
> #endif
>     {
>-      _Link_type __x = _M_begin();
>+      _Base_ptr __x = _M_begin();
>       _Base_ptr __y = _M_end();
>-      while (__x != 0)
>+      while (__x)
> 	{
> 	  __y = __x;
> 	  __x = !_M_impl._M_key_compare(_S_key(__x), _KeyOfValue()(__v)) ?
>@@ -1886,12 +2444,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   template<typename _Key, typename _Val, typename _KoV,
> 	   typename _Compare, typename _Alloc>
>     template<bool _MoveValues, typename _NodeGen>
>-      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Link_type
>+      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Base_ptr
>       _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::
>-      _M_copy(_Link_type __x, _Base_ptr __p, _NodeGen& __node_gen)
>+      _M_copy(_Base_ptr __x, _Base_ptr __p, _NodeGen& __node_gen)
>       {
> 	// Structural copy. __x and __p must be non-null.
>-	_Link_type __top = _M_clone_node<_MoveValues>(__x, __node_gen);
>+	_Base_ptr __top = _M_clone_node<_MoveValues>(__x, __node_gen);
> 	__top->_M_parent = __p;
>
> 	__try
>@@ -1902,9 +2460,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	    __p = __top;
> 	    __x = _S_left(__x);
>
>-	    while (__x != 0)
>+	    while (__x)
> 	      {
>-		_Link_type __y = _M_clone_node<_MoveValues>(__x, __node_gen);
>+		_Base_ptr __y = _M_clone_node<_MoveValues>(__x, __node_gen);
> 		__p->_M_left = __y;
> 		__y->_M_parent = __p;
> 		if (__x->_M_right)
>@@ -1926,13 +2484,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	   typename _Compare, typename _Alloc>
>     void
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>-    _M_erase(_Link_type __x)
>+    _M_erase(_Base_ptr __x)
>     {
>       // Erase without rebalancing.
>-      while (__x != 0)
>+      while (__x)
> 	{
> 	  _M_erase(_S_right(__x));
>-	  _Link_type __y = _S_left(__x);
>+	  _Base_ptr __y = _S_left(__x);
> 	  _M_drop_node(__x);
> 	  __x = __y;
> 	}
>@@ -1941,65 +2499,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   template<typename _Key, typename _Val, typename _KeyOfValue,
> 	   typename _Compare, typename _Alloc>
>     typename _Rb_tree<_Key, _Val, _KeyOfValue,
>-		      _Compare, _Alloc>::iterator
>+		      _Compare, _Alloc>::_Base_ptr
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>-    _M_lower_bound(_Link_type __x, _Base_ptr __y,
>-		   const _Key& __k)
>-    {
>-      while (__x != 0)
>-	if (!_M_impl._M_key_compare(_S_key(__x), __k))
>-	  __y = __x, __x = _S_left(__x);
>-	else
>-	  __x = _S_right(__x);
>-      return iterator(__y);
>-    }
>-
>-  template<typename _Key, typename _Val, typename _KeyOfValue,
>-	   typename _Compare, typename _Alloc>
>-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
>-		      _Compare, _Alloc>::const_iterator
>-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>-    _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
>+    _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
> 		   const _Key& __k) const
>     {
>-      while (__x != 0)
>+      while (__x)
> 	if (!_M_impl._M_key_compare(_S_key(__x), __k))
> 	  __y = __x, __x = _S_left(__x);
> 	else
> 	  __x = _S_right(__x);
>-      return const_iterator(__y);
>-    }
>-
>-  template<typename _Key, typename _Val, typename _KeyOfValue,
>-	   typename _Compare, typename _Alloc>
>-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
>-		      _Compare, _Alloc>::iterator
>-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>-    _M_upper_bound(_Link_type __x, _Base_ptr __y,
>-		   const _Key& __k)
>-    {
>-      while (__x != 0)
>-	if (_M_impl._M_key_compare(__k, _S_key(__x)))
>-	  __y = __x, __x = _S_left(__x);
>-	else
>-	  __x = _S_right(__x);
>-      return iterator(__y);
>+      return __y;
>     }
>
>   template<typename _Key, typename _Val, typename _KeyOfValue,
> 	   typename _Compare, typename _Alloc>
>     typename _Rb_tree<_Key, _Val, _KeyOfValue,
>-		      _Compare, _Alloc>::const_iterator
>+		      _Compare, _Alloc>::_Base_ptr
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>-    _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
>+    _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
> 		   const _Key& __k) const
>     {
>-      while (__x != 0)
>+      while (__x)
> 	if (_M_impl._M_key_compare(__k, _S_key(__x)))
> 	  __y = __x, __x = _S_left(__x);
> 	else
> 	  __x = _S_right(__x);
>-      return const_iterator(__y);
>+      return __y;
>     }
>
>   template<typename _Key, typename _Val, typename _KeyOfValue,
>@@ -2011,9 +2537,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>     equal_range(const _Key& __k)
>     {
>-      _Link_type __x = _M_begin();
>+      _Base_ptr __x = _M_begin();
>       _Base_ptr __y = _M_end();
>-      while (__x != 0)
>+      while (__x)
> 	{
> 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
> 	    __x = _S_right(__x);
>@@ -2021,13 +2547,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	    __y = __x, __x = _S_left(__x);
> 	  else
> 	    {
>-	      _Link_type __xu(__x);
>+	      _Base_ptr __xu(__x);
> 	      _Base_ptr __yu(__y);
> 	      __y = __x, __x = _S_left(__x);
> 	      __xu = _S_right(__xu);
>-	      return pair<iterator,
>-			  iterator>(_M_lower_bound(__x, __y, __k),
>-				    _M_upper_bound(__xu, __yu, __k));
>+	      return make_pair(iterator(_M_lower_bound(__x, __y, __k)),
>+			       iterator(_M_upper_bound(__xu, __yu, __k)));
> 	    }
> 	}
>       return pair<iterator, iterator>(iterator(__y),
>@@ -2043,9 +2568,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>     equal_range(const _Key& __k) const
>     {
>-      _Const_Link_type __x = _M_begin();
>-      _Const_Base_ptr __y = _M_end();
>-      while (__x != 0)
>+      _Base_ptr __x = _M_begin();
>+      _Base_ptr __y = _M_end();
>+      while (__x)
> 	{
> 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
> 	    __x = _S_right(__x);
>@@ -2053,13 +2578,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	    __y = __x, __x = _S_left(__x);
> 	  else
> 	    {
>-	      _Const_Link_type __xu(__x);
>-	      _Const_Base_ptr __yu(__y);
>+	      _Base_ptr __xu(__x);
>+	      _Base_ptr __yu(__y);
> 	      __y = __x, __x = _S_left(__x);
> 	      __xu = _S_right(__xu);
>-	      return pair<const_iterator,
>-			  const_iterator>(_M_lower_bound(__x, __y, __k),
>-					  _M_upper_bound(__xu, __yu, __k));
>+	      return make_pair(const_iterator(_M_lower_bound(__x, __y, __k)),
>+			       const_iterator(_M_upper_bound(__xu, __yu, __k)));
> 	    }
> 	}
>       return pair<const_iterator, const_iterator>(const_iterator(__y),
>@@ -2073,12 +2597,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     swap(_Rb_tree& __t)
>     _GLIBCXX_NOEXCEPT_IF(__is_nothrow_swappable<_Compare>::value)
>     {
>-      if (_M_root() == 0)
>+      if (!_M_root())
> 	{
>-	  if (__t._M_root() != 0)
>+	  if (__t._M_root())
> 	    _M_impl._M_move_data(__t._M_impl);
> 	}
>-      else if (__t._M_root() == 0)
>+      else if (!__t._M_root())
> 	__t._M_impl._M_move_data(_M_impl);
>       else
> 	{
>@@ -2093,8 +2617,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       // No need to swap header's color as it does not change.
>       std::swap(this->_M_impl._M_key_compare, __t._M_impl._M_key_compare);
>
>-      _Alloc_traits::_S_on_swap(_M_get_Node_allocator(),
>-				__t._M_get_Node_allocator());
>+      _Node_alloc_traits::_S_on_swap(_M_get_Node_allocator(),
>+				     __t._M_get_Node_allocator());
>     }
>
>   template<typename _Key, typename _Val, typename _KeyOfValue,
>@@ -2107,10 +2631,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _M_get_insert_unique_pos(const key_type& __k)
>     {
>       typedef pair<_Base_ptr, _Base_ptr> _Res;
>-      _Link_type __x = _M_begin();
>+      _Base_ptr __x = _M_begin();
>       _Base_ptr __y = _M_end();
>       bool __comp = true;
>-      while (__x != 0)
>+      while (__x)
> 	{
> 	  __y = __x;
> 	  __comp = _M_impl._M_key_compare(__k, _S_key(__x));
>@@ -2126,7 +2650,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	}
>       if (_M_impl._M_key_compare(_S_key(__j._M_node), __k))
> 	return _Res(__x, __y);
>-      return _Res(__j._M_node, 0);
>+      return _Res(__j._M_node, _Base_ptr());
>     }
>
>   template<typename _Key, typename _Val, typename _KeyOfValue,
>@@ -2139,9 +2663,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _M_get_insert_equal_pos(const key_type& __k)
>     {
>       typedef pair<_Base_ptr, _Base_ptr> _Res;
>-      _Link_type __x = _M_begin();
>+      _Base_ptr __x = _M_begin();
>       _Base_ptr __y = _M_end();
>-      while (__x != 0)
>+      while (__x)
> 	{
> 	  __y = __x;
> 	  __x = _M_impl._M_key_compare(__k, _S_key(__x)) ?
>@@ -2209,44 +2733,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _M_get_insert_hint_unique_pos(const_iterator __position,
> 				  const key_type& __k)
>     {
>-      iterator __pos = __position._M_const_cast();
>       typedef pair<_Base_ptr, _Base_ptr> _Res;
>
>       // end()
>-      if (__pos._M_node == _M_end())
>+      if (__position._M_node == _M_end())
> 	{
> 	  if (size() > 0
> 	      && _M_impl._M_key_compare(_S_key(_M_rightmost()), __k))
>-	    return _Res(0, _M_rightmost());
>+	    return _Res(_Base_ptr(), _M_rightmost());
> 	  else
> 	    return _M_get_insert_unique_pos(__k);
> 	}
>-      else if (_M_impl._M_key_compare(__k, _S_key(__pos._M_node)))
>+      else if (_M_impl._M_key_compare(__k, _S_key(__position._M_node)))
> 	{
> 	  // First, try before...
>-	  iterator __before = __pos;
>-	  if (__pos._M_node == _M_leftmost()) // begin()
>+	  iterator __before(__position._M_node);
>+	  if (__position._M_node == _M_leftmost()) // begin()
> 	    return _Res(_M_leftmost(), _M_leftmost());
> 	  else if (_M_impl._M_key_compare(_S_key((--__before)._M_node), __k))
> 	    {
>-	      if (_S_right(__before._M_node) == 0)
>-		return _Res(0, __before._M_node);
>+	      if (!_S_right(__before._M_node))
>+		return _Res(_Base_ptr(), __before._M_node);
> 	      else
>-		return _Res(__pos._M_node, __pos._M_node);
>+		return _Res(__position._M_node, __position._M_node);
> 	    }
> 	  else
> 	    return _M_get_insert_unique_pos(__k);
> 	}
>-      else if (_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
>+      else if (_M_impl._M_key_compare(_S_key(__position._M_node), __k))
> 	{
> 	  // ... then try after.
>-	  iterator __after = __pos;
>-	  if (__pos._M_node == _M_rightmost())
>-	    return _Res(0, _M_rightmost());
>+	  iterator __after(__position._M_node);
>+	  if (__position._M_node == _M_rightmost())
>+	    return _Res(_Base_ptr(), _M_rightmost());
> 	  else if (_M_impl._M_key_compare(__k, _S_key((++__after)._M_node)))
> 	    {
>-	      if (_S_right(__pos._M_node) == 0)
>-		return _Res(0, __pos._M_node);
>+	      if (!_S_right(__position._M_node))
>+		return _Res(_Base_ptr(), __position._M_node);
> 	      else
> 		return _Res(__after._M_node, __after._M_node);
> 	    }
>@@ -2255,7 +2778,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	}
>       else
> 	// Equivalent keys.
>-	return _Res(__pos._M_node, 0);
>+	return _Res(__position._M_node, _Base_ptr());
>     }
>
>   template<typename _Key, typename _Val, typename _KeyOfValue,
>@@ -2294,30 +2817,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>     _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
>     {
>-      iterator __pos = __position._M_const_cast();
>       typedef pair<_Base_ptr, _Base_ptr> _Res;
>
>       // end()
>-      if (__pos._M_node == _M_end())
>+      if (__position._M_node == _M_end())
> 	{
> 	  if (size() > 0
> 	      && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
>-	    return _Res(0, _M_rightmost());
>+	    return _Res(_Base_ptr(), _M_rightmost());
> 	  else
> 	    return _M_get_insert_equal_pos(__k);
> 	}
>-      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
>+      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
> 	{
> 	  // First, try before...
>-	  iterator __before = __pos;
>-	  if (__pos._M_node == _M_leftmost()) // begin()
>+	  iterator __before(__position._M_node);
>+	  if (__position._M_node == _M_leftmost()) // begin()
> 	    return _Res(_M_leftmost(), _M_leftmost());
> 	  else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
> 	    {
>-	      if (_S_right(__before._M_node) == 0)
>-		return _Res(0, __before._M_node);
>+	      if (!_S_right(__before._M_node))
>+		return _Res(_Base_ptr(), __before._M_node);
> 	      else
>-		return _Res(__pos._M_node, __pos._M_node);
>+		return _Res(__position._M_node, __position._M_node);
> 	    }
> 	  else
> 	    return _M_get_insert_equal_pos(__k);
>@@ -2325,18 +2847,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       else
> 	{
> 	  // ... then try after.
>-	  iterator __after = __pos;
>-	  if (__pos._M_node == _M_rightmost())
>-	    return _Res(0, _M_rightmost());
>+	  iterator __after(__position._M_node);
>+	  if (__position._M_node == _M_rightmost())
>+	    return _Res(_Base_ptr(), _M_rightmost());
> 	  else if (!_M_impl._M_key_compare(_S_key((++__after)._M_node), __k))
> 	    {
>-	      if (_S_right(__pos._M_node) == 0)
>-		return _Res(0, __pos._M_node);
>+	      if (!_S_right(__position._M_node))
>+		return _Res(_Base_ptr(), __position._M_node);
> 	      else
> 		return _Res(__after._M_node, __after._M_node);
> 	    }
> 	  else
>-	    return _Res(0, 0);
>+	    return _Res(_Base_ptr(), _Base_ptr());
> 	}
>     }
>
>@@ -2373,15 +2895,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	   typename _Compare, typename _Alloc>
>     auto
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>-    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Link_type __z)
>+    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Base_ptr __z)
>     -> iterator
>     {
>-      bool __insert_left = (__x != 0 || __p == _M_end()
>+      bool __insert_left = (__x || __p == _M_end()
> 			    || _M_impl._M_key_compare(_S_key(__z),
> 						      _S_key(__p)));
>
>-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
>-				    this->_M_impl._M_header);
>+      _Node_traits::_S_insert_and_rebalance
>+	(__insert_left, __z, __p, this->_M_impl._M_header);
>       ++_M_impl._M_node_count;
>       return iterator(__z);
>     }
>@@ -2390,15 +2912,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	   typename _Compare, typename _Alloc>
>     auto
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>-    _M_insert_lower_node(_Base_ptr __p, _Link_type __z)
>+    _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z)
>     -> iterator
>     {
>       bool __insert_left = (__p == _M_end()
> 			    || !_M_impl._M_key_compare(_S_key(__p),
> 						       _S_key(__z)));
>
>-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
>-				    this->_M_impl._M_header);
>+      _Node_traits::_S_insert_and_rebalance
>+	(__insert_left, __z, __p, this->_M_impl._M_header);
>       ++_M_impl._M_node_count;
>       return iterator(__z);
>     }
>@@ -2407,12 +2929,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	   typename _Compare, typename _Alloc>
>     auto
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>-    _M_insert_equal_lower_node(_Link_type __z)
>+    _M_insert_equal_lower_node(_Base_ptr __z)
>     -> iterator
>     {
>-      _Link_type __x = _M_begin();
>+      _Base_ptr __x = _M_begin();
>       _Base_ptr __y = _M_end();
>-      while (__x != 0)
>+      while (__x)
> 	{
> 	  __y = __x;
> 	  __x = !_M_impl._M_key_compare(_S_key(__x), _S_key(__z)) ?
>@@ -2487,10 +3009,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>     _M_erase_aux(const_iterator __position)
>     {
>-      _Link_type __y =
>-	static_cast<_Link_type>(_Rb_tree_rebalance_for_erase
>-				(const_cast<_Base_ptr>(__position._M_node),
>-				 this->_M_impl._M_header));
>+      _Base_ptr __y = _Node_traits::_S_rebalance_for_erase
>+	(__position._M_node, this->_M_impl._M_header);
>       _M_drop_node(__y);
>       --_M_impl._M_node_count;
>     }
>@@ -2527,7 +3047,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>     find(const _Key& __k)
>     {
>-      iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
>+      iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
>       return (__j == end()
> 	      || _M_impl._M_key_compare(__k,
> 					_S_key(__j._M_node))) ? end() : __j;
>@@ -2540,7 +3060,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
>     find(const _Key& __k) const
>     {
>-      const_iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
>+      const_iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
>       return (__j == end()
> 	      || _M_impl._M_key_compare(__k,
> 					_S_key(__j._M_node))) ? end() : __j;
>@@ -2574,9 +3094,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       unsigned int __len = _Rb_tree_black_count(_M_leftmost(), _M_root());
>       for (const_iterator __it = begin(); __it != end(); ++__it)
> 	{
>-	  _Const_Link_type __x = static_cast<_Const_Link_type>(__it._M_node);
>-	  _Const_Link_type __L = _S_left(__x);
>-	  _Const_Link_type __R = _S_right(__x);
>+	  _Base_ptr __x = __it._M_node;
>+	  _Base_ptr __L = _S_left(__x);
>+	  _Base_ptr __R = _S_right(__x);
>
> 	  if (__x->_M_color == _S_red)
> 	    if ((__L && __L->_M_color == _S_red)
>@@ -2592,9 +3112,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	    return false;
> 	}
>
>-      if (_M_leftmost() != _Rb_tree_node_base::_S_minimum(_M_root()))
>+      if (_M_leftmost() != _Node_base::_S_minimum(_M_root()))
> 	return false;
>-      if (_M_rightmost() != _Rb_tree_node_base::_S_maximum(_M_root()))
>+      if (_M_rightmost() != _Node_base::_S_maximum(_M_root()))
> 	return false;
>       return true;
>     }
>diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
>new file mode 100644
>index 00000000000..3d83738f86b
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
>@@ -0,0 +1,37 @@
>+// { dg-do run { target c++11 } }
>+
>+#include <map>
>+#include <memory>
>+#include <testsuite_hooks.h>
>+#include <testsuite_allocator.h>
>+
>+struct T { int i; };
>+
>+bool operator<(const T& l, const T& r)
>+{ return l.i < r.i; }
>+
>+struct L : std::less<T>
>+{ };
>+
>+using __gnu_test::CustomPointerAlloc;
>+
>+template class std::map<T, int, L,
>+			CustomPointerAlloc<std::pair<const T, int>>>;
>+
>+void test01()
>+{
>+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
>+  typedef std::map<T, int, L, alloc_type> test_type;
>+  test_type v;
>+  v.insert({ T(), 0 });
>+  VERIFY( v.begin()->second == 0 );
>+  VERIFY( ++v.begin() == v.end() );
>+  v.insert({ T { 1 }, 1 });
>+  VERIFY( v.size() == 2 );
>+  VERIFY( v.find(T()) != v.end() );
>+}
>+
>+int main()
>+{
>+  test01();
>+}
>diff --git a/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
>new file mode 100644
>index 00000000000..41689ef43b7
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
>@@ -0,0 +1,33 @@
>+// { dg-do run { target c++11 } }
>+
>+#include <map>
>+#include <memory>
>+#include <testsuite_hooks.h>
>+#include <testsuite_allocator.h>
>+
>+struct T { int i; };
>+
>+bool operator<(const T& l, const T& r)
>+{ return l.i < r.i; }
>+
>+struct L : std::less<T>
>+{ };
>+
>+using __gnu_test::CustomPointerAlloc;
>+
>+template class std::multimap<T, int, L,
>+			CustomPointerAlloc<std::pair<const T, int>>>;
>+
>+void test01()
>+{
>+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
>+  typedef std::multimap<T, int, L, alloc_type> test_type;
>+  test_type v;
>+  v.insert({ T(), 0 });
>+  VERIFY( ++v.begin() == v.end() );
>+}
>+
>+int main()
>+{
>+  test01();
>+}
>diff --git a/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
>new file mode 100644
>index 00000000000..589e6b0a617
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
>@@ -0,0 +1,32 @@
>+// { dg-do run { target c++11 } }
>+
>+#include <set>
>+#include <memory>
>+#include <testsuite_hooks.h>
>+#include <testsuite_allocator.h>
>+
>+struct T { int i; };
>+
>+bool operator<(const T& l, const T& r)
>+{ return l.i < r.i; }
>+
>+struct L : std::less<T>
>+{ };
>+
>+using __gnu_test::CustomPointerAlloc;
>+
>+template class std::multiset<T, L, CustomPointerAlloc<T>>;
>+
>+void test01()
>+{
>+  typedef CustomPointerAlloc<T> alloc_type;
>+  typedef std::multiset<T, L, alloc_type> test_type;
>+  test_type v;
>+  v.insert(T());
>+  VERIFY( ++v.begin() == v.end() );
>+}
>+
>+int main()
>+{
>+  test01();
>+}
>diff --git a/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
>new file mode 100644
>index 00000000000..bbcf32b20be
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
>@@ -0,0 +1,32 @@
>+// { dg-do run { target c++11 } }
>+
>+#include <set>
>+#include <memory>
>+#include <testsuite_hooks.h>
>+#include <testsuite_allocator.h>
>+
>+struct T { int i; };
>+
>+bool operator<(const T& l, const T& r)
>+{ return l.i < r.i; }
>+
>+struct L : std::less<T>
>+{ };
>+
>+using __gnu_test::CustomPointerAlloc;
>+
>+template class std::set<T, L, CustomPointerAlloc<T>>;
>+
>+void test01()
>+{
>+  typedef CustomPointerAlloc<T> alloc_type;
>+  typedef std::set<T, L, alloc_type> test_type;
>+  test_type v;
>+  v.insert(T());
>+  VERIFY( ++v.begin() == v.end() );
>+}
>+
>+int main()
>+{
>+  test01();
>+}
  
Jonathan Wakely Dec. 4, 2024, 10:06 p.m. UTC | #18
On Wed, 4 Dec 2024 at 21:50, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On 04/12/24 19:27 +0100, François Dumont wrote:
> >Hi
> >
> >I've completed the synchronization with your equivalent PR for
> >std::list so here is the updated patch.
> >
> >PR updated a couple of days ago.
> >
> >Note that I've started to rework the patch for the same in _Hashtable.
>
> Great, thanks.


Ugh, gmail completely mangles this mail for me.

You can see my comments and questions about changing _Link_type to
_Base_ptr here:

https://gcc.gnu.org/pipermail/gcc-patches/2024-December/670872.html


> >François
> >
> >
> >On 30/11/2024 18:21, Jonathan Wakely wrote:
> >>
> >>
> >>On Sat, 30 Nov 2024, 13:52 François Dumont, <frs.dumont@gmail.com> wrote:
> >>
> >>    Hi
> >>
> >>    I've applied all your comments below and the ones you did on the
> >>    PR directly.
> >>
> >>    When all new types totally seperated from the legacy types
> >>    _Rb_tree_node_traits is indeed useless.
> >>
> >>    Regarding _Rb_tree_helpers I got rid of it but move the
> >>    insert-and-rebalance and rebalance-for-erase function to
> >>    _Node_traits<>.
> >>
> >>    PR updated:
> >>
> >>    https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
> >>
> >>    libstdc++: Add fancy pointer support in map and set Support fancy
> >>    allocator pointer type in std::_Rb_tree<>. In case of fancy
> >>    pointer type the container is now storing the pointer to
> >>    __rb_tree::_Node<_ValPtr> as a pointer to
> >>    __rb_tree::_Node_base<_VoidPtr>. Many methods are adapted to take
> >>    and return _Base_ptr in place of _Link_type which has been renamed
> >>    into _Node_ptr. As all node are stored as _Base_ptr have all
> >>    methods working with this type and remove _Const_Base_ptr and
> >>    _Const_Node_ptr and all methods associated with it.
> >>
> >>
> >>Sounds good! I'll take another look on Monday.
> >>
> >>
> >>    libstdc++-v3/ChangeLog: * include/bits/stl_tree.h
> >>    [_GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE]: New macro to control usage
> >>    of the code required to support fancy allocator pointer type.
> >>    (_Rb_tree_node_base::_Const_Base_ptr): Remove.
> >>    (_Rb_tree_node_base::_S_minimum, _Rb_tree_node_base::_S_maximum):
> >>    Remove overloads for _Const_Base_ptr.
> >>    (_Rb_tree_node_base::_M_base_ptr()): New.
> >>    (_Rb_tree_node_base::_M_node_ptr<_NodePtr>()): New.
> >>    (_Rb_tree_node::_Link_type): Rename into...
> >>    (_Rb_tree_node::_Node_ptr): ...this. (__rb_tree::_Node_base<>):
> >>    New. (__rb_tree::_Header<>): New. (__rb_tree::_Node<>): New.
> >>    (_Rb_tree_increment(const _Rb_tree_node_base*)): Remove
> >>    declaration. (_Rb_tree_decrement(const _Rb_tree_node_base*)):
> >>    Remove declaration. (_Rb_tree_iterator<>::_Link_type): Rename
> >>    into... (_Rb_tree_iterator<>::_Node_ptr): ...this.
> >>    (_Rb_tree_const_iterator<>::_Link_type): Rename into...
> >>    (_Rb_tree_const_iterator<>::_Node_ptr): ...this.
> >>    (_Rb_tree_const_iterator<>::_M_const_cast): Remove.
> >>    (_Rb_tree_const_iterator<>::_M_node): Change type into _Base_ptr.
> >>    (__rb_tree::_Iterator<>): New. (__rb_tree::_Node_traits<>): New.
> >>    (_Rb_tree<>::_Node_base, _Rb_tree::_Node_type): New.
> >>    (_Rb_tree<>::_Link_type): Rename into... (_Rb_tree<>::_Node_ptr):
> >>    ...this. (_Rb_tree<>::_Const_Base_ptr,
> >>    _Rb_tree<>::_Const_Node_ptr): Remove. (_Rb_tree<>): Adapt to
> >>    generalize usage of _Base_ptr in place of former _Link_type now
> >>    _Node_ptr. (_Rb_tree<>::_M_mbegin): Remove.
> >>    (_Rb_tree<>::_S_left(_Const_Base_ptr)): Remove.
> >>    (_Rb_tree<>::_S_right(_Const_Base_ptr)): Remove.
> >>    (_Rb_tree<>::_S_maximum(_Const_Base_ptr)): Remove.
> >>    (_Rb_tree<>::_S_minimum(_Const_Base_ptr)): Remove. *
> >>    testsuite/23_containers/map/allocator/ext_ptr.cc: New test case. *
> >>    testsuite/23_containers/multimap/allocator/ext_ptr.cc: New test
> >>    case. * testsuite/23_containers/multiset/allocator/ext_ptr.cc: New
> >>    test case. * testsuite/23_containers/set/allocator/ext_ptr.cc: New
> >>    test case.
> >>
> >>    I've tested it in C++98/C++17/C++20 with default
> >>    _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE and with it 9001.
> >>
> >>    François
> >>
> >>    On 18/11/2024 17:39, Jonathan Wakely wrote:
> >>>    On 18/11/24 06:57 +0100, François Dumont wrote:
> >>>>    Hi
> >>>>
> >>>>    Here is a new proposal with all the cleanup regarding _Const_Base_ptr
> >>>>    that makes support of allocator's fancy pointer type simpler.
> >>>>
> >>>>    Also submitted as PR:
> >>>>
> >>>>    https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
> >>>>
> >>>>       libstdc++: Add fancy pointer support in map and set
> >>>>
> >>>>        Support fancy allocator pointer type instd::_Rb_tree<>.
> >>>>
> >>>>        In case of fancy pointer type the container is now storing the
> >>>>    pointer to
> >>>>        _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.
> >>>>
> >>>>        Many methods are adapted to take and return _Base_ptr in place of
> >>>>    _Link_type
> >>>>        which has been renamed into _Node_ptr.
> >>>>
> >>>>        As all node are stored as _Base_ptr have all methods working with
> >>>>    this type
> >>>>        and remove _Const_Base_ptr and all methods associated to it.
> >>>>
> >>>>        libstdc++-v3/ChangeLog:
> >>>>
> >>>>                * include/bits/stl_set.h (std::set<>::upper_bound<>(const
> >>>>    _Kt&) const): Fix
> >>>>                decltype typo to use const_iterator.
> >>>>                * include/bits/stl_tree.h
> >>>>                (_Rb_tree_ptr_traits<>): New.
> >>>>                (_Rb_tree_pnode_base<>): New.
> >>>>                (_Rb_tree_node_base): Inherit from latter.
> >>>>                (_Rb_tree_node_base::_Const_Base_ptr): Remove.
> >>>>                (_Rb_tree_node_base::_S_minimum(_Const_Base_ptr)): Remove.
> >>>>                (_Rb_tree_node_base::_S_maximum(_Const_Base_ptr)): Remove.
> >>>>                (_Rb_tree_pheader): New.
> >>>>                (_Rb_tree_header): Inherit from latter.
> >>>>                (_Rb_tree_node_val): New.
> >>>>                (_Rb_tree_node): Inherit from latter.
> >>>>                (_Rb_tree_pnode): New.
> >>>>                (_Rb_tree_iterator<>::_Link_type): Rename into...
> >>>>                (_Rb_tree_iterator<>::_Node_ptr): ...this.
> >>>>                (_Rb_tree_const_iterator<>::_Link_type): Rename into...
> >>>>                (_Rb_tree_const_iterator<>::_Node_ptr): ...this.
> >>>>                (_Rb_tree_const_iterator<>::_M_node): Change type into
> >>>>    _Base_ptr.
> >>>>                (_Rb_tree_const_iterator<>::_M_const_cast): Remove.
> >>>>                (_Rb_tree_helpers<>): New.
> >>>>                (_Rb_tree_piterator): New.
> >>>>                (_Rb_tree_const_piterator): New.
> >>>>                (_Rb_tree_node_traits<>): New.
> >>>>                (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
> >>>>                (_Rb_tree<>::_Const_Base_ptr): Remove.
> >>>>                (_Rb_tree): Adapt to generalize usage of _Base_ptr in
> >>>>    place of _Link_type.
> >>>>                (_Rb_tree<>::_M_mbegin): Remove.
> >>>>                (_Rb_tree<>::_S_left(_Const_Base_ptr)): Remove.
> >>>>                (_Rb_tree<>::_S_right(_Const_Base_ptr)): Remove.
> >>>>                (_Rb_tree<>::_S_maximum(_Const_Base_ptr)): Remove.
> >>>>                (_Rb_tree<>::_S_minimum(_Const_Base_ptr)): Remove.
> >>>>                * testsuite/23_containers/map/allocator/ext_ptr.cc: New
> >>>>    test case.
> >>>>                * testsuite/23_containers/multimap/allocator/ext_ptr.cc:
> >>>>    New test case.
> >>>>                * testsuite/23_containers/multiset/allocator/ext_ptr.cc:
> >>>>    New test case.
> >>>>                * testsuite/23_containers/set/allocator/ext_ptr.cc: New
> >>>>    test case.
> >>>>
> >>>>    Tested under Linux x64.
> >>>>
> >>>>
> >>>>    Note that I've also run the 23_containers tests on map, multimap,
> >>>>    multiset and set tweaking implementation
> >>>>    so that new types are being used when C++11 or later even if allocator
> >>>>    pointer type is a C pointer.
> >>>    Please take a look at the _GLIBCXX_USE_ALLOC_PTR_xxx macros in
> >>>    https://gcc.gnu.org/pipermail/gcc-patches/2024-November/669128.html
> >>>    andhttps://gcc.gnu.org/pipermail/gcc-patches/2024-November/669127.html
> >>>    Those macros allow the fancy pointer support to be disabled, in case
> >>>    it will break anything for current users. Maybe more importantly, it
> >>>    allows the new node types to be used always, even for T*. That means
> >>>    we can run the entire testsuite using the new nodes without tweaking
> >>>    the implementation.
> >>>
> >>>    Please consider a _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE macro (or some
> >>>    other name).
> >>>
> >>>>    Ok to commit ?
> >>>>
> >>>>    François
> >>>>
> >>>>    On 12/11/2024 15:56, Jonathan Wakely wrote:
> >>>>>    On Mon, 4 Nov 2024 at 21:34, François Dumont<frs.dumont@gmail.com> <mailto:frs.dumont@gmail.com> wrote:
> >>>>>>    On 04/11/2024 19:45, Jonathan Wakely wrote:
> >>>>>>>    On Mon, 4 Nov 2024 at 18:30, François Dumont<frs.dumont@gmail.com> <mailto:frs.dumont@gmail.com> wrote:
> >>>>>>>>    On 21/10/2024 06:56, François Dumont wrote:
> >>>>>>>>
> >>>>>>>>
> >>>>>>>>    On 17/10/2024 23:11, Jonathan Wakely wrote:
> >>>>>>>>
> >>>>>>>>
> >>>>>>>>
> >>>>>>>>    On Thu, 17 Oct 2024 at 21:39, Jonathan Wakely<jwakely.gcc@gmail.com> <mailto:jwakely.gcc@gmail.com> wrote:
> >>>>>>>>>    On Thu, 17 Oct 2024 at 20:52, François Dumont<frs.dumont@gmail.com> <mailto:frs.dumont@gmail.com> wrote:
> >>>>>>>>>>    Here is an updated version that compiles, I think, all your feedbacks. It's much cleaner indeed.
> >>>>>>>>>    Thanks, I'll take a look tomorrow.
> >>>>>>>>>
> >>>>>>>>>>    It's also tested in C++98/17/23.
> >>>>>>>>>>
> >>>>>>>>>>    I'm surprised that we do not need to consider potentialallocator::const_pointer.
> >>>>>>>>>    Do you mean consider the case whereAlloc::const_pointer is not the same type as rebinding 'pointer' to a const element type?
> >>>>>>>>    Yes, exactly.
> >>>>>>>>
> >>>>>>>>
> >>>>>>>>>    We don't need to consider that because we never get a 'const_pointer' from the allocator, and we never need to pass a 'const_pointer' to the allocator. The allocator's 'allocate' and 'deallocate' members both work with the 'pointer' type, so we only need to use that type when interacting with the allocator. For all the other uses, such as _Const_Node_ptr, what we need is a pointer-to-const that's compatible with the allocator's pointer type. It doesn't actually matter if it's the same type as allocator_traits<Alloc>::const_pointer, because we don't need
> >>>>>>>>    Sorry, I sent the email before finishing that thought!
> >>>>>>>>
> >>>>>>>>    ... we don't need to pass a const_pointer to anything, we only need it for the container's own purposes.
> >>>>>>>>
> >>>>>>>>    But thinking about it some more, do we even need a const-pointer for the container?  Currently the const_iterator stores a const-pointer, and some members like _M_root() and _M_leftmost() return a const-pointer. But they don't need to. The nodes are all pointed to by a non-const _Base_ptr, none of the storage managed by the container is const.
> >>>>>>>>
> >>>>>>>>    Except _M_impl._M_header in a const context.
> >>>>>>>>
> >>>>>>>>    Using const_cast would result in a similar UB that you fixed on _GLIBCXX_DEBUG containers, no ?
> >>>>>>>    We only use a pointer to _M_header for the past-the-end iterator,
> >>>>>>>    which is not dereferenceable. It's OK to do the const_cast, it's not
> >>>>>>>    OK to write something through that pointer/reference if the object is
> >>>>>>>    really const. But you can't write through a past-the-end pointer
> >>>>>>>    anyway.
> >>>>>>>
> >>>>>>    One last question then to complete this work, is it fine to change
> >>>>>>    _Rb_tree_const_iterator::_M_node to _Base_ptr ? No abi issue in doing so ?
> >>>>>    I thought I'd sent a reply to this question, but I don't see it in the
> >>>>>    archives, sorry.
> >>>>>
> >>>>>    So changing from _Const_Base_ptr to _Base_ptr? Yes, I think that's OK.
> >>>>>
> >>>>    diff --git a/libstdc++-v3/include/bits/stl_set.h b/libstdc++-v3/include/bits/stl_set.h
> >>>>    index c0eb4dbf65f..3d17626f330 100644
> >>>>    --- a/libstdc++-v3/include/bits/stl_set.h
> >>>>    +++ b/libstdc++-v3/include/bits/stl_set.h
> >>>>    @@ -875,7 +875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> >>>>           template<typename _Kt>
> >>>>         auto
> >>>>         upper_bound(const _Kt& __x) const
> >>>>    -    -> decltype(iterator(_M_t._M_upper_bound_tr(__x)))
> >>>>    +    -> decltype(const_iterator(_M_t._M_upper_bound_tr(__x)))
> >>>>         { return const_iterator(_M_t._M_upper_bound_tr(__x)); }
> >>>>    #endif
> >>>>           ///@}
> >>>>    diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
> >>>>    index bc27e191e8b..c6cba99726d 100644
> >>>>    --- a/libstdc++-v3/include/bits/stl_tree.h
> >>>>    +++ b/libstdc++-v3/include/bits/stl_tree.h
> >>>>    @@ -96,44 +96,100 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>
> >>>>       enum _Rb_tree_color { _S_red = false, _S_black = true };
> >>>>
> >>>>    -  struct _Rb_tree_node_base
> >>>>    -  {
> >>>>    -    typedef _Rb_tree_node_base* _Base_ptr;
> >>>>    -    typedef const _Rb_tree_node_base* _Const_Base_ptr;
> >>>>    -
> >>>>    -    _Rb_tree_color    _M_color;
> >>>>    -    _Base_ptr        _M_parent;
> >>>>    -    _Base_ptr        _M_left;
> >>>>    -    _Base_ptr        _M_right;
> >>>>    -
> >>>>    -    static _Base_ptr
> >>>>    -    _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >>>>    +#if __cplusplus >= 201103L
> >>>>    +  template<typename _Ptr>
> >>>>    +    struct _Rb_tree_ptr_traits
> >>>>         {
> >>>>    -      while (__x->_M_left != 0) __x = __x->_M_left;
> >>>>    -      return __x;
> >>>>    -    }
> >>>>    +      template<typename _Tp>
> >>>>    +    struct __rebind
> >>>>    +    { using __type = __ptr_rebind<_Ptr, _Tp>; };
> >>>    This only seems to be used in one place, can you just use __ptr_rebind
> >>>    directly in that one place?
> >>>
> >>>>    +
> >>>>    +      template<typename _BasePtr>
> >>>>    +    static _Ptr
> >>>>    +    _S_ptr_to(_BasePtr __this) noexcept
> >>>>    +    { return pointer_traits<_Ptr>::pointer_to(*__this); }
> >>>    This is also only needed in exactly one place. I avoided needing this
> >>>    function by adding _M_base_ptr() to the node base classes.
> >>>
> >>>    The whole of this _Rb_tree_ptr_traits class template seems unnecessary
> >>>    to me. Just inline the code directly into the places it's needed.
> >>>    You're not optimizing anything by replacing
> >>>    pointer_traits<T*>::pointer_to with
> >>>    _Rb_tree_ptr_traits<T*>::_S_ptr_to, it's still a class template
> >>>    specialization and a function call.
> >>>
> >>>>    +
> >>>>    +      template<typename _BasePtr, typename _UpPtr>
> >>>>    +    static _UpPtr
> >>>>    +    _S_up_ptr_to(_BasePtr __this) noexcept
> >>>    pointer_to is not guaranteed to be noexcept.
> >>>
> >>>    This appears to be a downcast, i.e. a base-to-derived cast, having
> >>>    "up" in the name is a bit confusing.
> >>>
> >>>>    +    {
> >>>>    +      using __ptr_traits = pointer_traits<_UpPtr>;
> >>>>    +      using __up_type = typename __ptr_traits::element_type;
> >>>>    +      auto __up_this = static_cast<__up_type*>(__this);
> >>>>    +      return __ptr_traits::pointer_to(*__up_this);
> >>>>    +    }
> >>>>    +    };
> >>>>    +#else
> >>>>    +  template<typename _Ptr>
> >>>>    +    struct _Rb_tree_ptr_traits;
> >>>>    +#endif
> >>>>
> >>>>    -    static _Const_Base_ptr
> >>>>    -    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >>>>    +  template<typename _Tp>
> >>>>    +    struct _Rb_tree_ptr_traits<_Tp*>
> >>>>         {
> >>>>    -      while (__x->_M_left != 0) __x = __x->_M_left;
> >>>>    -      return __x;
> >>>>    -    }
> >>>>    +      template<typename>
> >>>>    +    struct __rebind
> >>>>    +    { typedef _Tp* __type; };
> >>>>    +
> >>>>    +      template<typename _BasePtr>
> >>>>    +    static _Tp*
> >>>>    +    _S_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
> >>>>    +    { return static_cast<_Tp*>(__this); }
> >>>>    +
> >>>>    +      template<typename _BasePtr, typename _UpPtr>
> >>>>    +    static _UpPtr
> >>>>    +    _S_up_ptr_to(_BasePtr __this) _GLIBCXX_NOEXCEPT
> >>>>    +    { return static_cast<_UpPtr>(__this); }
> >>>>    +    };
> >>>>
> >>>>    -    static _Base_ptr
> >>>>    -    _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >>>>    +  template<typename _BasePtr>
> >>>>    +    struct _Rb_tree_pnode_base
> >>>>         {
> >>>>    -      while (__x->_M_right != 0) __x = __x->_M_right;
> >>>>    -      return __x;
> >>>>    -    }
> >>>>    +      typedef typename _Rb_tree_ptr_traits<_BasePtr>::template
> >>>>    +    __rebind<_Rb_tree_pnode_base>::__type _Base_ptr;
> >>>    The names _BasePtr and _Base_ptr are very similar, and confusing.
> >>>    It looks like sometimes the template argument that is substituted for
> >>>    _BasePtr is sometimes a derived class (_Rb_tree_node_base) and
> >>>    sometimes is _ValPtr. It's never a "base" pointer. Please rename it.
> >>>
> >>>>    -    static _Const_Base_ptr
> >>>>    -    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >>>>    -    {
> >>>>    -      while (__x->_M_right != 0) __x = __x->_M_right;
> >>>>    -      return __x;
> >>>>    -    }
> >>>>    -  };
> >>>>    +      _Base_ptr
> >>>>    +      _M_self_ptr() _GLIBCXX_NOEXCEPT
> >>>    Why do we need a non-const overload? The const one will work for all
> >>>    cases, right?
> >>>
> >>>    I added a comment to my list patches pointing out that it's not
> >>>    const-correct, but is safe as long as only used carefully.
> >>>
> >>>
> >>>>    +      { return _Rb_tree_ptr_traits<_Base_ptr>::_S_ptr_to(this); }
> >>>>    +
> >>>>    +      _Base_ptr
> >>>>    +      _M_self_ptr() const _GLIBCXX_NOEXCEPT
> >>>>    +      {
> >>>>    +    _Rb_tree_pnode_base* __self = const_cast<_Rb_tree_pnode_base*>(this);
> >>>>    +    return __self->_M_self_ptr();
> >>>>    +      }
> >>>>    +
> >>>>    +      template<typename _NodePtr>
> >>>>    +    _NodePtr
> >>>>    +    _M_node_ptr() _GLIBCXX_NOEXCEPT
> >>>>    +    {
> >>>>    +      return _Rb_tree_ptr_traits<_Base_ptr>::template
> >>>>    +        _S_up_ptr_to<_Rb_tree_pnode_base*, _NodePtr>(this);
> >>>>    +    }
> >>>>    +
> >>>>    +      _Rb_tree_color    _M_color;
> >>>>    +      _Base_ptr        _M_parent;
> >>>>    +      _Base_ptr        _M_left;
> >>>>    +      _Base_ptr        _M_right;
> >>>>    +
> >>>>    +      static _Base_ptr
> >>>>    +      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >>>>    +      {
> >>>>    +    while (__x->_M_left) __x = __x->_M_left;
> >>>>    +    return __x;
> >>>>    +      }
> >>>>    +
> >>>>    +      static _Base_ptr
> >>>>    +      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >>>>    +      {
> >>>>    +    while (__x->_M_right) __x = __x->_M_right;
> >>>>    +    return __x;
> >>>>    +      }
> >>>>    +    };
> >>>>    +
> >>>>    +  struct _Rb_tree_node_base
> >>>>    +    : _Rb_tree_pnode_base<_Rb_tree_node_base*>
> >>>>    +  { };
> >>>    This approaches means that the existing specializations of RB trees
> >>>    all get twice as many base classes. I don't see an ABI problem, and it
> >>>    should generate the same code for optimized builds, but increasing the
> >>>    inheritance depth and the number of class template instantiations
> >>>    gives the compiler more work to do, compiling slower, and generating
> >>>    additional debug info (and maybe RTTI?)
> >>>
> >>>    Please take a look at how I did it for the linked lists, where
> >>>    specializations for non-fancy pointers, e.g. _List_node<T>, are mostly
> >>>    unchanged. They don't get any new base classes, just some new typedefs
> >>>    and new accessors like:
> >>>
> >>>    _Base_ptr _M_base() { return this; }
> >>>    const _List_node* _M_base() const { return this; }
> >>>
> >>>    The _List_node_traits class template is used bystd::list etc. to
> >>>    select whether to use _List_node<Alloc::value_type> or
> >>>    __list::_Node<Alloc::pointer>. All the fancy pointer support is in the
> >>>    new type hierarchy, __list::_Node_base, __list::_Node, and
> >>>    __list::_Iterator. The old types like _List_node don't really change.
> >>>
> >>>    I found that using the __list namespace with new types reduced a lot
> >>>    of confusion I had between the very similar names _List_node and
> >>>    _List_pnode (or _List_ptr_node, or other names like that).
> >>>
> >>>    Maybe we could call it _List::_Node instead of __list::_Node, so it's
> >>>    a bit closer to _List_node but still distinguishable (and would
> >>>    produce very slightly shorter mangled names). I didn't do that,
> >>>    because our convention is to name impl namespaces as __foo not _Foo.
> >>>
> >>>    That pattern could naturally be extended to __tree::_Node (or
> >>>    _Tree::_Node) as well.
> >>>
> >>>>       // Helper type offering value initialization guarantee on the compare functor.
> >>>>       template<typename _Key_compare>
> >>>>    @@ -163,81 +219,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>         };
> >>>>
> >>>>       // Helper type to manage default initialization of node count and header.
> >>>>    -  struct _Rb_tree_header
> >>>>    -  {
> >>>>    -    _Rb_tree_node_base    _M_header;
> >>>>    -    size_t        _M_node_count; // Keeps track of size of tree.
> >>>>    -
> >>>>    -    _Rb_tree_header() _GLIBCXX_NOEXCEPT
> >>>>    +  template<typename _NodeBase>
> >>>>    +    struct _Rb_tree_pheader
> >>>>         {
> >>>>    -      _M_header._M_color = _S_red;
> >>>>    -      _M_reset();
> >>>>    -    }
> >>>>    +    private:
> >>>>    +      typedef typename _NodeBase::_Base_ptr        _Base_ptr;
> >>>>    +
> >>>>    +    public:
> >>>>    +      _NodeBase        _M_header;
> >>>>    +      size_t        _M_node_count; // Keeps track of size of tree.
> >>>>    +
> >>>>    +      _Rb_tree_pheader() _GLIBCXX_NOEXCEPT
> >>>>    +      {
> >>>>    +    _M_header._M_color = _S_red;
> >>>>    +    _M_reset();
> >>>>    +      }
> >>>>
> >>>>    #if __cplusplus >= 201103L
> >>>>    -    _Rb_tree_header(_Rb_tree_header&& __x) noexcept
> >>>>    -    {
> >>>>    -      if (__x._M_header._M_parent != nullptr)
> >>>>    -    _M_move_data(__x);
> >>>>    -      else
> >>>>    -    {
> >>>>    -      _M_header._M_color = _S_red;
> >>>>    -      _M_reset();
> >>>>    -    }
> >>>>    -    }
> >>>>    +      _Rb_tree_pheader(_Rb_tree_pheader&& __x) noexcept
> >>>>    +      {
> >>>>    +    if (__x._M_header._M_parent)
> >>>>    +      _M_move_data(__x);
> >>>>    +    else
> >>>>    +      {
> >>>>    +        _M_header._M_color = _S_red;
> >>>>    +        _M_reset();
> >>>>    +      }
> >>>>    +      }
> >>>>    #endif
> >>>>
> >>>>    -    void
> >>>>    -    _M_move_data(_Rb_tree_header& __from)
> >>>>    -    {
> >>>>    -      _M_header._M_color = __from._M_header._M_color;
> >>>>    -      _M_header._M_parent = __from._M_header._M_parent;
> >>>>    -      _M_header._M_left = __from._M_header._M_left;
> >>>>    -      _M_header._M_right = __from._M_header._M_right;
> >>>>    -      _M_header._M_parent->_M_parent = &_M_header;
> >>>>    -      _M_node_count = __from._M_node_count;
> >>>>    -
> >>>>    -      __from._M_reset();
> >>>>    -    }
> >>>>    +      void
> >>>>    +      _M_move_data(_Rb_tree_pheader& __from)
> >>>>    +      {
> >>>>    +    _M_header._M_color = __from._M_header._M_color;
> >>>>    +    _M_header._M_parent = __from._M_header._M_parent;
> >>>>    +    _M_header._M_left = __from._M_header._M_left;
> >>>>    +    _M_header._M_right = __from._M_header._M_right;
> >>>>    +    _M_header._M_parent->_M_parent = _M_header._M_self_ptr();
> >>>>    +    _M_node_count = __from._M_node_count;
> >>>>    +
> >>>>    +    __from._M_reset();
> >>>>    +      }
> >>>>
> >>>>    -    void
> >>>>    -    _M_reset()
> >>>>    -    {
> >>>>    -      _M_header._M_parent = 0;
> >>>>    -      _M_header._M_left = &_M_header;
> >>>>    -      _M_header._M_right = &_M_header;
> >>>>    -      _M_node_count = 0;
> >>>>    -    }
> >>>>    -  };
> >>>>    +      void
> >>>>    +      _M_reset()
> >>>>    +      {
> >>>>    +    _M_header._M_parent = _Base_ptr();
> >>>>    +    _M_header._M_left = _M_header._M_right = _M_header._M_self_ptr();
> >>>>    +    _M_node_count = 0;
> >>>>    +      }
> >>>>    +    };
> >>>>    +
> >>>>    +  struct _Rb_tree_header : _Rb_tree_pheader<_Rb_tree_node_base>
> >>>>    +  { };
> >>>>
> >>>>       template<typename _Val>
> >>>>    -    struct _Rb_tree_node : public _Rb_tree_node_base
> >>>>    +    struct _Rb_tree_node_val
> >>>>         {
> >>>>    -      typedef _Rb_tree_node<_Val>* _Link_type;
> >>>>    -
> >>>>    #if __cplusplus < 201103L
> >>>>           _Val _M_value_field;
> >>>>
> >>>>    +      __attribute__((__always_inline__))
> >>>>           _Val*
> >>>>           _M_valptr()
> >>>>           { returnstd::__addressof(_M_value_field); }
> >>>>
> >>>>    +      __attribute__((__always_inline__))
> >>>>           const _Val*
> >>>>           _M_valptr() const
> >>>>           { returnstd::__addressof(_M_value_field); }
> >>>>    #else
> >>>>           __gnu_cxx::__aligned_membuf<_Val> _M_storage;
> >>>    For the new __list::_Node type I took the opportunity to replace
> >>>    __aligned_membuf with a union { _Val _M_data; }; which is simpler and
> >>>    produces the same code. We could probably make that change for the
> >>>    existing node types, I think it's safe, but making it only for the new
> >>>    _Node type means the old node type isn't affected.
> >>>
> >>>
> >>>>    +      [[__gnu__::__always_inline__]]
> >>>>           _Val*
> >>>>           _M_valptr()
> >>>>           { return _M_storage._M_ptr(); }
> >>>>
> >>>>    +      [[__gnu__::__always_inline__]]
> >>>>           const _Val*
> >>>>           _M_valptr() const
> >>>>           { return _M_storage._M_ptr(); }
> >>>>    #endif
> >>>>         };
> >>>>
> >>>>    +  template<typename _Val>
> >>>>    +    struct _Rb_tree_node
> >>>>    +    : _Rb_tree_node_base
> >>>>    +    , _Rb_tree_node_val<_Val>
> >>>>    +    {
> >>>>    +      typedef _Rb_tree_node<_Val>* _Node_ptr;
> >>>>    +    };
> >>>>    +
> >>>>    +#if __cplusplus >= 201103L
> >>>>    +  template<typename _ValPtr>
> >>>>    +    struct _Rb_tree_pnode
> >>>>    +    : _Rb_tree_pnode_base<_ValPtr>
> >>>    This makes _Rb_tree_pnode_base depend on the value_type, which means
> >>>    you instantiate a different specialization of _Rb_tree_pnode_base for
> >>>    every _Rb_tree. There's no point even having a common base class if
> >>>    every specialization is different. You might as well just put the
> >>>    _M_parent/_M_left/_M_right pointers in _Rb_tree_pnode directly.
> >>>
> >>>    The point of the node_base class is to have a common base that is not
> >>>    dependent on the value type, so thatstd::set<int> andstd::set<long>
> >>>    andstd::set<int, Cmp> all use the same node base. The way this patch
> >>>    works,std::set<int> will use _Rb_tree_pnode_base<int*> and
> >>>    std::set<long> will use _Rb_tree_pnode_base<long*>.
> >>>
> >>>    This is why my patch for lists uses __ptr_rebind<_ValPtr, void> as the
> >>>    template argument to _Node_base, so that they all use
> >>>    _Node_base<void*>.
> >>>
> >>>    Obviously for a fancy pointer, it will be a different specialization,
> >>>    sostd::list<int, A<int>> uses _Node_base<A::void_pointer>, but
> >>>    std::list<long, A<long>> uses the same _Node_base<A::void_pointer>.
> >>>
> >>>>    +    , _Rb_tree_node_val<typenamestd::pointer_traits<_ValPtr>::element_type>
> >>>>    +    {
> >>>>    +      using _Node_ptr = __ptr_rebind<_ValPtr, _Rb_tree_pnode>;
> >>>>    +    };
> >>>>    +#endif
> >>>>    +
> >>>>       _GLIBCXX_PURE _Rb_tree_node_base*
> >>>>       _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
> >>>>
> >>>    [...]
> >>>
> >>>>    +#if __cplusplus >= 201103L
> >>>>    +  template<typename _ValPtr>
> >>>>    +    struct _Rb_tree_piterator
> >>>>    +    {
> >>>>    +      using __ptr_traits = pointer_traits<_ValPtr>;
> >>>>    +      using value_type = typename __ptr_traits::element_type;
> >>>>    +      using reference = value_type&;
> >>>>    +      using pointer = value_type*;
> >>>>    +
> >>>>    +      typedef bidirectional_iterator_tag iterator_category;
> >>>>    +      typedef ptrdiff_t             difference_type;
> >>>>    +
> >>>>    +      typedef _Rb_tree_piterator            _Self;
> >>>>    +      typedef _Rb_tree_pnode_base<_ValPtr>        _Node_base;
> >>>>    +      typedef _Rb_tree_pnode<_ValPtr>            _Node_type;
> >>>>    +      typedef typename _Node_type::_Base_ptr        _Base_ptr;
> >>>>    +      typedef typename _Node_type::_Node_ptr        _Node_ptr;
> >>>>    +      typedef _Rb_tree_helpers<_Node_base>        _Tree_helpers;
> >>>>    +
> >>>>    +      _Rb_tree_piterator() _GLIBCXX_NOEXCEPT
> >>>>    +      : _M_node() { }
> >>>>    +
> >>>>    +      explicit
> >>>>    +      _Rb_tree_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >>>>    +      : _M_node(__x) { }
> >>>>    +
> >>>>    +      reference
> >>>>    +      operator*() const _GLIBCXX_NOEXCEPT
> >>>>    +      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
> >>>>    +
> >>>>    +      pointer
> >>>>    +      operator->() const _GLIBCXX_NOEXCEPT
> >>>>    +      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
> >>>>    +
> >>>>    +      _Self&
> >>>>    +      operator++() _GLIBCXX_NOEXCEPT
> >>>>    +      {
> >>>>    +    _M_node = _Tree_helpers::_Increment(_M_node);
> >>>>    +    return *this;
> >>>>    +      }
> >>>>    +
> >>>>    +      _Self
> >>>>    +      operator++(int) _GLIBCXX_NOEXCEPT
> >>>>    +      {
> >>>>    +    _Self __tmp = *this;
> >>>>    +    _M_node = _Tree_helpers::_Increment(_M_node);
> >>>>    +    return __tmp;
> >>>>    +      }
> >>>>    +
> >>>>    +      _Self&
> >>>>    +      operator--() _GLIBCXX_NOEXCEPT
> >>>>    +      {
> >>>>    +    _M_node = _Tree_helpers::_Decrement(_M_node);
> >>>>    +    return *this;
> >>>>    +      }
> >>>>    +
> >>>>    +      _Self
> >>>>    +      operator--(int) _GLIBCXX_NOEXCEPT
> >>>>    +      {
> >>>>    +    _Self __tmp = *this;
> >>>>    +    _M_node = _Tree_helpers::_Decrement(_M_node);
> >>>>    +    return __tmp;
> >>>>    +      }
> >>>>    +
> >>>>    +      friend bool
> >>>>    +      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
> >>>>    +      { return __x._M_node == __y._M_node; }
> >>>>    +
> >>>>    +#if ! __cpp_lib_three_way_comparison
> >>>>    +      friend bool
> >>>>    +      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
> >>>>    +      { return __x._M_node != __y._M_node; }
> >>>>    +#endif
> >>>>    +
> >>>>    +      _Base_ptr _M_node;
> >>>>    +    };
> >>>>    +
> >>>>    +  template<typename _ValPtr>
> >>>>    +    struct _Rb_tree_const_piterator
> >>>    For the linked lists, I didn't add two new iterator types, just
> >>>    _Iterator<_Const, _ValPtr> which is used for iterator and
> >>>    const_iterator.
> >>>
> >>>    It seemed nice not to duplicate the iterator type, since the iterator
> >>>    and const_iterator are 95% the same. If we don't need to support
> >>>    C++98, we can write a single iterator implementation.
> >>>
> >>>    If you have a single iterator implementation, you probably don't need
> >>>    the _Rb_tree_helpers.
> >>>
> >>>    There would still be uses of insert-and-rebalance and
> >>>    rebalance-for-erase, but those could just be new overloads for fancy
> >>>    pointers. I don't think we need the _Rb_tree_helpers class template.
> >>>
> >>>
> >>>>    +    {
> >>>>    +      using __ptr_traits = pointer_traits<_ValPtr>;
> >>>>    +      using value_type = typename __ptr_traits::element_type;
> >>>>    +      using reference = const value_type&;
> >>>>    +      using pointer = const value_type*;
> >>>>    +
> >>>>    +      typedef _Rb_tree_piterator<_ValPtr>        iterator;
> >>>>    +      typedef typenameiterator::_Base_ptr        _Base_ptr;
> >>>>    +
> >>>>    +      typedef bidirectional_iterator_tag iterator_category;
> >>>>    +      typedef ptrdiff_t             difference_type;
> >>>>    +
> >>>>    +      typedef _Rb_tree_const_piterator            _Self;
> >>>>    +      typedef _Rb_tree_pnode_base<_ValPtr>        _Node_base;
> >>>>    +      typedef _Rb_tree_pnode<_ValPtr>            _Node_type;
> >>>>    +      typedef _Rb_tree_helpers<_Node_base>        _Tree_helpers;
> >>>>    +
> >>>>    +      _Rb_tree_const_piterator() _GLIBCXX_NOEXCEPT
> >>>>    +      : _M_node() { }
> >>>>    +
> >>>>    +      explicit
> >>>>    +      _Rb_tree_const_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >>>>    +      : _M_node(__x) { }
> >>>>    +
> >>>>    +      _Rb_tree_const_piterator(const iterator& __it) _GLIBCXX_NOEXCEPT
> >>>>    +      : _M_node(__it._M_node) { }
> >>>>    +
> >>>>    +      reference
> >>>>    +      operator*() const _GLIBCXX_NOEXCEPT
> >>>>    +      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
> >>>>    +
> >>>>    +      pointer
> >>>>    +      operator->() const _GLIBCXX_NOEXCEPT
> >>>>    +      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
> >>>>    +
> >>>>    +      _Self&
> >>>>    +      operator++() _GLIBCXX_NOEXCEPT
> >>>>    +      {
> >>>>    +    _M_node = _Tree_helpers::_Increment(_M_node);
> >>>    You need the iterator increment code here, but you can define it
> >>>    inline without the _Increment helper.
> >>>
> >>>>    +    return *this;
> >>>>    +      }
> >>>>    +
> >>>>    +      _Self
> >>>>    +      operator++(int) _GLIBCXX_NOEXCEPT
> >>>>    +      {
> >>>>    +    _Self __tmp = *this;
> >>>>    +    _M_node = _Tree_helpers::_Increment(_M_node);
> >>>    This could just use ++*this. So if there's no separate const iterator
> >>>    implementation, there's no more use of _Increment.
> >>>
> >>>>    +    return __tmp;
> >>>>    +      }
> >>>>    +
> >>>>    +      _Self&
> >>>>    +      operator--() _GLIBCXX_NOEXCEPT
> >>>>    +      {
> >>>>    +    _M_node = _Tree_helpers::_Decrement(_M_node);
> >>>>    +    return *this;
> >>>>    +      }
> >>>>    +
> >>>>    +      _Self
> >>>>    +      operator--(int) _GLIBCXX_NOEXCEPT
> >>>>    +      {
> >>>>    +    _Self __tmp = *this;
> >>>>    +    _M_node = _Tree_helpers::_Decrement(_M_node);
> >>>>    +    return __tmp;
> >>>>    +      }
> >>>>    +
> >>>>    +      friend bool
> >>>>    +      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
> >>>>    +      { return __x._M_node == __y._M_node; }
> >>>>    +
> >>>>    +#if ! __cpp_lib_three_way_comparison
> >>>>    +      friend bool
> >>>>    +      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
> >>>>    +      { return __x._M_node != __y._M_node; }
> >>>>    +#endif
> >>>>    +
> >>>>    +      _Base_ptr _M_node;
> >>>>    +    };
> >>>>    +#endif // C++11
> >>>>    +
> >>>>    +#if __cplusplus >= 201103L
> >>>>    +  template<typename _ValPtr>
> >>>>    +    struct _Rb_tree_node_traits
> >>>>    +    {
> >>>>    +      using _Node_base        = _Rb_tree_pnode_base<_ValPtr>;
> >>>>    +      using _Node_type        = _Rb_tree_pnode<_ValPtr>;
> >>>>    +      using _Header_t        = _Rb_tree_pheader<_Node_base>;
> >>>>    +      using _Iterator_t        = _Rb_tree_piterator<_ValPtr>;
> >>>>    +      using _Const_iterator_t    = _Rb_tree_const_piterator<_ValPtr>;
> >>>>    +    };
> >>>>    +#else
> >>>>    +  template<typename _ValPtr>
> >>>>    +    struct _Rb_tree_node_traits;
> >>>>    +#endif
> >>>>    +
> >>>>    +  template<typename _Val>
> >>>>    +    struct _Rb_tree_node_traits<_Val*>
> >>>>    +    {
> >>>>    +      typedef _Rb_tree_node_base        _Node_base;
> >>>>    +      typedef _Rb_tree_node<_Val>        _Node_type;
> >>>>    +      typedef _Rb_tree_header            _Header_t;
> >>>>    +      typedef _Rb_tree_iterator<_Val>        _Iterator_t;
> >>>>    +      typedef _Rb_tree_const_iterator<_Val>    _Const_iterator_t;
> >>>>    +    };
> >>>>
> >>>>    #if __cplusplus > 201402L
> >>>>       template<typename _Tree1, typename _Cmp2>
> >>>>    @@ -424,16 +1068,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>            typename _Compare, typename _Alloc = allocator<_Val> >
> >>>>         class _Rb_tree
> >>>>         {
> >>>>    +      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::pointer _ValPtr;
> >>>>    +      typedef _Rb_tree_node_traits<_ValPtr> _Node_traits;
> >>>>    +
> >>>>    +      typedef typename _Node_traits::_Node_base        _Node_base;
> >>>>    +      typedef typename _Node_traits::_Node_type        _Node_type;
> >>>>    +
> >>>>           typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
> >>>>    -    rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
> >>>>    +    rebind<_Node_type>::other _Node_allocator;
> >>>>
> >>>>           typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
> >>>>
> >>>>         protected:
> >>>>    -      typedef _Rb_tree_node_base*         _Base_ptr;
> >>>>    -      typedef const _Rb_tree_node_base*     _Const_Base_ptr;
> >>>>    -      typedef _Rb_tree_node<_Val>*         _Link_type;
> >>>>    -      typedef const _Rb_tree_node<_Val>*    _Const_Link_type;
> >>>>    +      typedef typename _Node_base::_Base_ptr         _Base_ptr;
> >>>>    +      typedef typename _Node_type::_Node_ptr         _Node_ptr;
> >>>    Accessing a member of _Node_type here requires _Node_type to be
> >>>    complete, which requires _Tp to be complete. That's allowed by the
> >>>    standard, becausestd::map andstd::set don't support incomplete types
> >>>    (unlike vector and the linked lists).
> >>>
> >>>    But we don't really ned to instantiate _Node_type here, we could use
> >>>    _Alloc_traits::pointer instead.
> >>>
> >>>
> >>>    [...]
> >>>
> >>>>    @@ -1826,10 +2427,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>                       || _M_impl._M_key_compare(_KeyOfValue()(__v),
> >>>>                                 _S_key(__p)));
> >>>>
> >>>>    -    _Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
> >>>>    +    _Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
> >>>    I think this relies on implicit conversion from _Node_ptr to
> >>>    _Base_ptr, which is not necessarily possible. It works for the
> >>>    _Pointer_adapter type in <ext/pointer.h> because it has a
> >>>    template<typename U> _Pointer_adapter(_Pointer_adapter<U>) constructor
> >>>    that supports implicit conversions.
> >>>
> >>>    There seems to be a few other places with this problem. Please take a
> >>>    look at the Pointer type in the new test for my list and forward_list
> >>>    fancy ptr patches (that Pointer should probably be added to the
> >>>    testsuite/util/testsuite_allocator.h header for reuse).
> >>>
> >>>    I solved these problems by adding a _M_base_ptr() function to the
> >>>    node_base type, which means that instead of relying on an implicit
> >>>    conversion from node_ptr to _Base_ptr you call node_ptr->_M_base_ptr()
> >>>    to get a _Base_ptr.
> >>>
> >>>>    @@ -2255,7 +2821,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>         }
> >>>>           else
> >>>>         // Equivalent keys.
> >>>>    -    return _Res(__pos._M_node, 0);
> >>>>    +    return _Res(__position._M_node, 0);
> >>>>         }
> >>>>
> >>>>       template<typename _Key, typename _Val, typename _KeyOfValue,
> >>>>    @@ -2294,11 +2860,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>         _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >>>>         _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
> >>>>         {
> >>>>    -      iterator __pos = __position._M_const_cast();
> >>>    This removes __pos ...
> >>>
> >>>>           typedef pair<_Base_ptr, _Base_ptr> _Res;
> >>>>
> >>>>           // end()
> >>>>    -      if (__pos._M_node == _M_end())
> >>>>    +      if (__position._M_node == _M_end())
> >>>>         {
> >>>>           if (size() > 0
> >>>>               && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
> >>>>    @@ -2306,18 +2871,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>           else
> >>>>             return _M_get_insert_equal_pos(__k);
> >>>>         }
> >>>>    -      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
> >>>>    +      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
> >>>>         {
> >>>>           // First, try before...
> >>>>           iterator __before = __pos;
> >>>    But it's still used here.
> >>>
> >>>>    -      if (__pos._M_node == _M_leftmost()) // begin()
> >>>>    +      if (__position._M_node == _M_leftmost()) // begin()
> >>>>             return _Res(_M_leftmost(), _M_leftmost());
> >>>>           else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
> >>>>             {
> >>>>               if (_S_right(__before._M_node) == 0)
> >>>>             return _Res(0, __before._M_node);
> >>>>               else
> >>>>    -        return _Res(__pos._M_node, __pos._M_node);
> >>>>    +        return _Res(__position._M_node, __position._M_node);
> >>>>             }
> >>>>           else
> >>>>             return _M_get_insert_equal_pos(__k);
> >>>>    @@ -2326,12 +2891,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>         {
> >>>>           // ... then try after.
> >>>>           iterator __after = __pos;
> >>>    And here.
> >>>
> >>>    I think they should both be __position._M_const_cast().
> >>>
> >>>
> >>>>    diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
> >>>>    new file mode 100644
> >>>>    index 00000000000..758beedccfd
> >>>>    --- /dev/null
> >>>>    +++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
> >>>>    @@ -0,0 +1,44 @@
> >>>>    +// { dg-do run { target c++11 } }
> >>>>    +
> >>>>    +#include <map>
> >>>>    +#include <memory>
> >>>>    +#include <testsuite_hooks.h>
> >>>>    +#include <testsuite_allocator.h>
> >>>>    +
> >>>>    +struct T { int i; };
> >>>>    +
> >>>>    +bool operator<(const T& l, const T& r)
> >>>>    +{ return l.i < r.i; }
> >>>>    +
> >>>>    +struct H
> >>>    This struct H occurs in several tests but seems to be unused.
> >>>
> >>>>    +{
> >>>>    +std::size_t
> >>>>    +  operator()(const T& t) const noexcept
> >>>>    +  { return t.i; }
> >>>>    +};
> >>>>    +
> >>>>    +struct L :std::less<T>
> >>>>    +{ };
> >>>>    +
> >>>>    +using __gnu_test::CustomPointerAlloc;
> >>>>    +
> >>>>    +template classstd::map<T, int, L,
> >>>>    +            CustomPointerAlloc<std::pair<const T, int>>>;
> >>>>    +
> >>>>    +void test01()
> >>>>    +{
> >>>>    +  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
> >>>>    +  typedefstd::map<T, int, L, alloc_type> test_type;
> >>>>    +  test_type v;
> >>>>    +  v.insert({ T(), 0 });
> >>>>    +  VERIFY( v.begin()->second == 0 );
> >>>>    +  VERIFY( ++v.begin() == v.end() );
> >>>>    +  v.insert({ T { 1 }, 1 });
> >>>>    +  VERIFY( v.size() == 2 );
> >>>>    +  VERIFY( v.find(T()) != v.end() );
> >>>>    +}
> >>>>    +
> >>>>    +int main()
> >>>>    +{
> >>>>    +  test01();
> >>>>    +}
> >>>    I'd be interested on your opinion on my patches for fancy pointer
> >>>    support in the linked lists. I think it's cleaner and requires fewer
> >>>    changes to the existing specializations.
> >>>
> >>>    My changes can also be disabled by a macro (to avoid breaking any
> >>>    users who are somehow managing to use allocators-with-fancy-pointers
> >>>    with RB trees today, which could be useful unless that's completely
> >>>    broken today), and to be enabled for non-fancy pointers (for test
> >>>    coverage).
> >>>
>
> >diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
> >index bc27e191e8b..bed7266c688 100644
> >--- a/libstdc++-v3/include/bits/stl_tree.h
> >+++ b/libstdc++-v3/include/bits/stl_tree.h
> >@@ -66,6 +66,7 @@
> > #include <bits/allocator.h>
> > #include <bits/stl_function.h>
> > #include <bits/cpp_type_traits.h>
> >+#include <bits/ptr_traits.h>
> > #include <ext/alloc_traits.h>
> > #if __cplusplus >= 201103L
> > # include <ext/aligned_buffer.h>
> >@@ -74,6 +75,13 @@
> > # include <bits/node_handle.h>
> > #endif
> >
> >+#if __cplusplus < 201103L
> >+# undef _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
> >+# define _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE 0
> >+#elif ! defined _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
> >+# define _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE 1
> >+#endif
> >+
> > namespace std _GLIBCXX_VISIBILITY(default)
> > {
> > _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >@@ -99,7 +107,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >   struct _Rb_tree_node_base
> >   {
> >     typedef _Rb_tree_node_base* _Base_ptr;
> >-    typedef const _Rb_tree_node_base* _Const_Base_ptr;
> >
> >     _Rb_tree_color    _M_color;
> >     _Base_ptr         _M_parent;
> >@@ -113,13 +120,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       return __x;
> >     }
> >
> >-    static _Const_Base_ptr
> >-    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >-    {
> >-      while (__x->_M_left != 0) __x = __x->_M_left;
> >-      return __x;
> >-    }
> >-
> >     static _Base_ptr
> >     _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >     {
> >@@ -127,12 +127,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       return __x;
> >     }
> >
> >-    static _Const_Base_ptr
> >-    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >-    {
> >-      while (__x->_M_right != 0) __x = __x->_M_right;
> >-      return __x;
> >-    }
> >+    // This is not const-correct, but it's only used in a const access path
> >+    // by std::_Rb_tree::_M_end() where the pointer is used to initialize a
> >+    // const_iterator and so constness is restored.
> >+    _Base_ptr
> >+    _M_base_ptr() const _GLIBCXX_NOEXCEPT
> >+    { return const_cast<_Rb_tree_node_base*>(this); }
> >+
> >+    template<typename _NodePtr>
> >+      _NodePtr
> >+      _M_node_ptr() _GLIBCXX_NOEXCEPT
> >+      { return static_cast<_NodePtr>(this); }
> >   };
> >
> >   // Helper type offering value initialization guarantee on the compare functor.
> >@@ -213,7 +218,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >   template<typename _Val>
> >     struct _Rb_tree_node : public _Rb_tree_node_base
> >     {
> >-      typedef _Rb_tree_node<_Val>* _Link_type;
> >+      typedef _Rb_tree_node<_Val>* _Node_ptr;
> >
> > #if __cplusplus < 201103L
> >       _Val _M_value_field;
> >@@ -238,18 +243,136 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > #endif
> >     };
> >
> >+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
> >+namespace __rb_tree
> >+{
> >+  template<typename _VoidPtr>
> >+    struct _Node_base
> >+    {
> >+      using _Base_ptr = __ptr_rebind<_VoidPtr, _Node_base>;
> >+
> >+      _Rb_tree_color  _M_color;
> >+      _Base_ptr               _M_parent;
> >+      _Base_ptr               _M_left;
> >+      _Base_ptr               _M_right;
> >+
> >+      static _Base_ptr
> >+      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >+      {
> >+      while (__x->_M_left) __x = __x->_M_left;
> >+      return __x;
> >+      }
> >+
> >+      static _Base_ptr
> >+      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >+      {
> >+      while (__x->_M_right) __x = __x->_M_right;
> >+      return __x;
> >+      }
> >+
> >+      // This is not const-correct, but it's only used in a const access path
> >+      // by std::_Rb_tree::_M_end() where the pointer is used to initialize a
> >+      // const_iterator and so constness is restored.
> >+      _Base_ptr
> >+      _M_base_ptr() const
> >+      {
> >+      return pointer_traits<_Base_ptr>::pointer_to
> >+        (*const_cast<_Node_base*>(this));
> >+      }
> >+
> >+      template<typename _NodePtr>
> >+      _NodePtr
> >+      _M_node_ptr() _GLIBCXX_NOEXCEPT
> >+      {
> >+        using __ptr_traits = pointer_traits<_NodePtr>;
> >+        using __node_type = typename __ptr_traits::element_type;
> >+        auto __this = static_cast<__node_type*>(this);
> >+        return __ptr_traits::pointer_to(*__this);
> >+      }
> >+    };
> >+
> >+  // Helper type to manage default initialization of node count and header.
> >+  template<typename _NodeBase>
> >+    struct _Header
> >+    {
> >+    private:
> >+      using _Base_ptr =  typename _NodeBase::_Base_ptr;
> >+
> >+    public:
> >+      _NodeBase               _M_header;
> >+      size_t          _M_node_count; // Keeps track of size of tree.
> >+
> >+      _Header() noexcept
> >+      {
> >+      _M_header._M_color = _S_red;
> >+      _M_reset();
> >+      }
> >+
> >+      _Header(_Header&& __x) noexcept
> >+      {
> >+      if (__x._M_header._M_parent)
> >+        _M_move_data(__x);
> >+      else
> >+        {
> >+          _M_header._M_color = _S_red;
> >+          _M_reset();
> >+        }
> >+      }
> >+
> >+      void
> >+      _M_move_data(_Header& __from)
> >+      {
> >+      _M_header._M_color = __from._M_header._M_color;
> >+      _M_header._M_parent = __from._M_header._M_parent;
> >+      _M_header._M_left = __from._M_header._M_left;
> >+      _M_header._M_right = __from._M_header._M_right;
> >+      _M_header._M_parent->_M_parent = _M_header._M_base_ptr();
> >+      _M_node_count = __from._M_node_count;
> >+
> >+      __from._M_reset();
> >+      }
> >+
> >+      void
> >+      _M_reset()
> >+      {
> >+      _M_header._M_parent = _Base_ptr{};
> >+      _M_header._M_left = _M_header._M_right = _M_header._M_base_ptr();
> >+      _M_node_count = 0;
> >+      }
> >+    };
> >+
> >+  template<typename _ValPtr>
> >+    struct _Node : public __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>
> >+    {
> >+      using value_type = typename pointer_traits<_ValPtr>::element_type;
> >+      using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
> >+
> >+      union
> >+      {
> >+      value_type _M_storage;
> >+      };
> >+
> >+      _Node() { }
> >+      ~_Node() { }
> >+      _Node(_Node&&) = delete;
> >+
> >+      value_type*
> >+      _M_valptr()
> >+      { return std::__addressof(_M_storage); }
> >+
> >+      value_type const*
> >+      _M_valptr() const
> >+      { return std::__addressof(_M_storage); }
> >+    };
> >+} // namespace __rb_tree
> >+#endif
> >+
> >   _GLIBCXX_PURE _Rb_tree_node_base*
> >   _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
> >
> >-  _GLIBCXX_PURE const _Rb_tree_node_base*
> >-  _Rb_tree_increment(const _Rb_tree_node_base* __x) throw ();
> >-
> >   _GLIBCXX_PURE _Rb_tree_node_base*
> >   _Rb_tree_decrement(_Rb_tree_node_base* __x) throw ();
> >
> >-  _GLIBCXX_PURE const _Rb_tree_node_base*
> >-  _Rb_tree_decrement(const _Rb_tree_node_base* __x) throw ();
> >-
> >   template<typename _Tp>
> >     struct _Rb_tree_iterator
> >     {
> >@@ -260,9 +383,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       typedef bidirectional_iterator_tag iterator_category;
> >       typedef ptrdiff_t                        difference_type;
> >
> >-      typedef _Rb_tree_iterator<_Tp>          _Self;
> >       typedef _Rb_tree_node_base::_Base_ptr   _Base_ptr;
> >-      typedef _Rb_tree_node<_Tp>*             _Link_type;
> >+      typedef _Rb_tree_node<_Tp>*             _Node_ptr;
> >
> >       _Rb_tree_iterator() _GLIBCXX_NOEXCEPT
> >       : _M_node() { }
> >@@ -273,49 +395,51 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> >       reference
> >       operator*() const _GLIBCXX_NOEXCEPT
> >-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
> >+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
> >
> >       pointer
> >       operator->() const _GLIBCXX_NOEXCEPT
> >-      { return static_cast<_Link_type> (_M_node)->_M_valptr(); }
> >+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
> >
> >-      _Self&
> >+      _Rb_tree_iterator&
> >       operator++() _GLIBCXX_NOEXCEPT
> >       {
> >       _M_node = _Rb_tree_increment(_M_node);
> >       return *this;
> >       }
> >
> >-      _Self
> >+      _Rb_tree_iterator
> >       operator++(int) _GLIBCXX_NOEXCEPT
> >       {
> >-      _Self __tmp = *this;
> >+      _Rb_tree_iterator __tmp = *this;
> >       _M_node = _Rb_tree_increment(_M_node);
> >       return __tmp;
> >       }
> >
> >-      _Self&
> >+      _Rb_tree_iterator&
> >       operator--() _GLIBCXX_NOEXCEPT
> >       {
> >       _M_node = _Rb_tree_decrement(_M_node);
> >       return *this;
> >       }
> >
> >-      _Self
> >+      _Rb_tree_iterator
> >       operator--(int) _GLIBCXX_NOEXCEPT
> >       {
> >-      _Self __tmp = *this;
> >+      _Rb_tree_iterator __tmp = *this;
> >       _M_node = _Rb_tree_decrement(_M_node);
> >       return __tmp;
> >       }
> >
> >       friend bool
> >-      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
> >+      operator==(const _Rb_tree_iterator& __x,
> >+               const _Rb_tree_iterator& __y) _GLIBCXX_NOEXCEPT
> >       { return __x._M_node == __y._M_node; }
> >
> > #if ! __cpp_lib_three_way_comparison
> >       friend bool
> >-      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
> >+      operator!=(const _Rb_tree_iterator& __x,
> >+               const _Rb_tree_iterator& __y) _GLIBCXX_NOEXCEPT
> >       { return __x._M_node != __y._M_node; }
> > #endif
> >
> >@@ -334,9 +458,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       typedef bidirectional_iterator_tag iterator_category;
> >       typedef ptrdiff_t                        difference_type;
> >
> >-      typedef _Rb_tree_const_iterator<_Tp>            _Self;
> >-      typedef _Rb_tree_node_base::_Const_Base_ptr     _Base_ptr;
> >-      typedef const _Rb_tree_node<_Tp>*                       _Link_type;
> >+      typedef _Rb_tree_node_base::_Base_ptr   _Base_ptr;
> >+      typedef const _Rb_tree_node<_Tp>*               _Node_ptr;
> >
> >       _Rb_tree_const_iterator() _GLIBCXX_NOEXCEPT
> >       : _M_node() { }
> >@@ -348,55 +471,53 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       _Rb_tree_const_iterator(const iterator& __it) _GLIBCXX_NOEXCEPT
> >       : _M_node(__it._M_node) { }
> >
> >-      iterator
> >-      _M_const_cast() const _GLIBCXX_NOEXCEPT
> >-      { return iterator(const_cast<typename iterator::_Base_ptr>(_M_node)); }
> >-
> >       reference
> >       operator*() const _GLIBCXX_NOEXCEPT
> >-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
> >+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
> >
> >       pointer
> >       operator->() const _GLIBCXX_NOEXCEPT
> >-      { return static_cast<_Link_type>(_M_node)->_M_valptr(); }
> >+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
> >
> >-      _Self&
> >+      _Rb_tree_const_iterator&
> >       operator++() _GLIBCXX_NOEXCEPT
> >       {
> >       _M_node = _Rb_tree_increment(_M_node);
> >       return *this;
> >       }
> >
> >-      _Self
> >+      _Rb_tree_const_iterator
> >       operator++(int) _GLIBCXX_NOEXCEPT
> >       {
> >-      _Self __tmp = *this;
> >+      _Rb_tree_const_iterator __tmp = *this;
> >       _M_node = _Rb_tree_increment(_M_node);
> >       return __tmp;
> >       }
> >
> >-      _Self&
> >+      _Rb_tree_const_iterator&
> >       operator--() _GLIBCXX_NOEXCEPT
> >       {
> >       _M_node = _Rb_tree_decrement(_M_node);
> >       return *this;
> >       }
> >
> >-      _Self
> >+      _Rb_tree_const_iterator
> >       operator--(int) _GLIBCXX_NOEXCEPT
> >       {
> >-      _Self __tmp = *this;
> >+      _Rb_tree_const_iterator __tmp = *this;
> >       _M_node = _Rb_tree_decrement(_M_node);
> >       return __tmp;
> >       }
> >
> >       friend bool
> >-      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
> >+      operator==(const _Rb_tree_const_iterator& __x,
> >+               const _Rb_tree_const_iterator& __y) _GLIBCXX_NOEXCEPT
> >       { return __x._M_node == __y._M_node; }
> >
> > #if ! __cpp_lib_three_way_comparison
> >       friend bool
> >-      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
> >+      operator!=(const _Rb_tree_const_iterator& __x,
> >+               const _Rb_tree_const_iterator& __y) _GLIBCXX_NOEXCEPT
> >       { return __x._M_node != __y._M_node; }
> > #endif
> >
> >@@ -415,6 +536,485 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >   _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
> >                              _Rb_tree_node_base& __header) throw ();
> >
> >+namespace __rb_tree
> >+{
> >+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
> >+  template<bool _Const, typename _ValPtr>
> >+    struct _Iterator
> >+    {
> >+      template<typename _Tp>
> >+      using __maybe_const = __conditional_t<_Const, const _Tp, _Tp>;
> >+
> >+      using __ptr_traits =    pointer_traits<_ValPtr>;
> >+      using value_type =      typename __ptr_traits::element_type;
> >+      using reference =               __maybe_const<value_type>&;
> >+      using pointer =         __maybe_const<value_type>*;
> >+
> >+      using iterator_category =       bidirectional_iterator_tag;
> >+      using difference_type = ptrdiff_t;
> >+
> >+      using _Node = __rb_tree::_Node<_ValPtr>;
> >+      using _Node_base = __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>;
> >+      using _Base_ptr =        typename _Node_base::_Base_ptr;
> >+
> >+      _Iterator() _GLIBCXX_NOEXCEPT
> >+      : _M_node() { }
> >+
> >+      constexpr explicit
> >+      _Iterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >+      : _M_node(__x) { }
> >+
> >+#if __cplusplus >= 201103L
> >+      _Iterator(const _Iterator&) = default;
> >+      _Iterator& operator=(const _Iterator&) = default;
> >+#endif
> >+
> >+#ifdef __glibcxx_concepts
> >+      constexpr
> >+      _Iterator(const _Iterator<false, _ValPtr>& __it) requires _Const
> >+#else
> >+      template<bool _OtherConst,
> >+             typename = __enable_if_t<_Const && !_OtherConst>>
> >+      constexpr
> >+      _Iterator(const _Iterator<_OtherConst, _ValPtr>& __it)
> >+#endif
> >+      : _M_node(__it._M_node) { }
> >+
> >+      [[__nodiscard__]]
> >+      reference
> >+      operator*() const _GLIBCXX_NOEXCEPT
> >+      { return *static_cast<_Node&>(*_M_node)._M_valptr(); }
> >+
> >+      [[__nodiscard__]]
> >+      pointer
> >+      operator->() const _GLIBCXX_NOEXCEPT
> >+      { return static_cast<_Node&>(*_M_node)._M_valptr(); }
> >+
> >+      _GLIBCXX14_CONSTEXPR _Iterator&
> >+      operator++() _GLIBCXX_NOEXCEPT
> >+      {
> >+      if (_M_node->_M_right)
> >+        {
> >+          _M_node = _M_node->_M_right;
> >+          while (_M_node->_M_left)
> >+            _M_node = _M_node->_M_left;
> >+        }
> >+      else
> >+        {
> >+          _Base_ptr __y = _M_node->_M_parent;
> >+          while (_M_node == __y->_M_right)
> >+            {
> >+              _M_node = __y;
> >+              __y = __y->_M_parent;
> >+            }
> >+          if (_M_node->_M_right != __y)
> >+            _M_node = __y;
> >+        }
> >+
> >+      return *this;
> >+      }
> >+
> >+      _GLIBCXX14_CONSTEXPR _Iterator
> >+      operator++(int) _GLIBCXX_NOEXCEPT
> >+      {
> >+      _Iterator __tmp(this->_M_node);
> >+      ++*this;
> >+      return __tmp;
> >+      }
> >+
> >+      _GLIBCXX14_CONSTEXPR _Iterator&
> >+      operator--() _GLIBCXX_NOEXCEPT
> >+      {
> >+      if (_M_node->_M_color == _S_red
> >+          && _M_node->_M_parent->_M_parent == _M_node)
> >+        _M_node = _M_node->_M_right;
> >+      else if (_M_node->_M_left)
> >+        {
> >+          _Base_ptr __y = _M_node->_M_left;
> >+          while (__y->_M_right)
> >+            __y = __y->_M_right;
> >+          _M_node = __y;
> >+        }
> >+      else
> >+        {
> >+          _Base_ptr __y = _M_node->_M_parent;
> >+          while (_M_node == __y->_M_left)
> >+            {
> >+              _M_node = __y;
> >+              __y = __y->_M_parent;
> >+            }
> >+          _M_node = __y;
> >+        }
> >+      return *this;
> >+      }
> >+
> >+      _GLIBCXX14_CONSTEXPR _Iterator
> >+      operator--(int) _GLIBCXX_NOEXCEPT
> >+      {
> >+      _Iterator __tmp(this->_M_node);
> >+      --*this;
> >+      return __tmp;
> >+      }
> >+
> >+      [[__nodiscard__]]
> >+      friend bool
> >+      operator==(const _Iterator& __x, const _Iterator& __y) _GLIBCXX_NOEXCEPT
> >+      { return __x._M_node == __y._M_node; }
> >+
> >+#if ! __cpp_lib_three_way_comparison
> >+      [[__nodiscard__]]
> >+      friend bool
> >+      operator!=(const _Iterator& __x, const _Iterator& __y) _GLIBCXX_NOEXCEPT
> >+      { return __x._M_node != __y._M_node; }
> >+#endif
> >+
> >+      _Base_ptr _M_node;
> >+    };
> >+#endif // USE_ALLOC_PTR_FOR_RB_TREE
> >+
> >+  // Determine the node and iterator types used by std::_Rb_tree.
> >+  template<typename _Val, typename _Ptr>
> >+    struct _Node_traits;
> >+
> >+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE <= 9000
> >+  // Specialization for the simple case where the allocator's pointer type
> >+  // is the same type as value_type*.
> >+  // For ABI compatibility we can't change the types used for this case.
> >+  template<typename _Val>
> >+    struct _Node_traits<_Val, _Val*>
> >+    {
> >+      typedef _Rb_tree_node<_Val>             _Node;
> >+      typedef _Rb_tree_node_base              _Node_base;
> >+      typedef _Rb_tree_header                 _Header_t;
> >+      typedef _Rb_tree_iterator<_Val>         _Iterator;
> >+      typedef _Rb_tree_const_iterator<_Val>   _Const_iterator;
> >+
> >+      __attribute__((__nonnull__))
> >+      static void
> >+      _S_insert_and_rebalance(const bool __insert_left,
> >+                            _Node_base* __x, _Node_base* __p,
> >+                            _Node_base& __header) _GLIBCXX_USE_NOEXCEPT
> >+      {
> >+      return _Rb_tree_insert_and_rebalance(__insert_left, __x, __p, __header);
> >+      }
> >+
> >+      __attribute__((__nonnull__,__returns_nonnull__))
> >+      static _Node_base*
> >+      _S_rebalance_for_erase(_Node_base* const __z,
> >+                           _Node_base& __header) _GLIBCXX_USE_NOEXCEPT
> >+      { return _Rb_tree_rebalance_for_erase(__z, __header); }
> >+    };
> >+#endif
> >+
> >+#if ! _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
> >+  // Always use the T* specialization.
> >+  template<typename _Val, typename _Ptr>
> >+    struct _Node_traits
> >+    : _Node_traits<_Val, _Val*>
> >+    { };
> >+#else
> >+  // Primary template used when the allocator uses fancy pointers.
> >+  template<typename _Val, typename _ValPtr>
> >+    struct _Node_traits
> >+    {
> >+      using _Node = __rb_tree::_Node<_ValPtr>;
> >+      using _Node_base = __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>;
> >+      using _Header_t = __rb_tree::_Header<_Node_base>;
> >+      using _Iterator = __rb_tree::_Iterator<false, _ValPtr>;
> >+      using _Const_iterator = __rb_tree::_Iterator<true, _ValPtr>;
> >+
> >+      using _Base_ptr = typename _Node_base::_Base_ptr;
> >+
> >+      static void
> >+      _Rotate_left(_Base_ptr __x, _Base_ptr& __root)
> >+      {
> >+      const _Base_ptr __y = __x->_M_right;
> >+
> >+      __x->_M_right = __y->_M_left;
> >+      if (__y->_M_left)
> >+        __y->_M_left->_M_parent = __x;
> >+      __y->_M_parent = __x->_M_parent;
> >+
> >+      if (__x == __root)
> >+        __root = __y;
> >+      else if (__x == __x->_M_parent->_M_left)
> >+        __x->_M_parent->_M_left = __y;
> >+      else
> >+        __x->_M_parent->_M_right = __y;
> >+      __y->_M_left = __x;
> >+      __x->_M_parent = __y;
> >+      }
> >+
> >+      static void
> >+      _Rotate_right(_Base_ptr __x, _Base_ptr& __root)
> >+      {
> >+      const _Base_ptr __y = __x->_M_left;
> >+
> >+      __x->_M_left = __y->_M_right;
> >+      if (__y->_M_right)
> >+        __y->_M_right->_M_parent = __x;
> >+      __y->_M_parent = __x->_M_parent;
> >+
> >+      if (__x == __root)
> >+        __root = __y;
> >+      else if (__x == __x->_M_parent->_M_right)
> >+        __x->_M_parent->_M_right = __y;
> >+      else
> >+        __x->_M_parent->_M_left = __y;
> >+      __y->_M_right = __x;
> >+      __x->_M_parent = __y;
> >+      }
> >+
> >+      static void
> >+      _S_insert_and_rebalance(const bool __insert_left,
> >+                            _Base_ptr __x, _Base_ptr __p,
> >+                            _Node_base& __header)
> >+      {
> >+      _Base_ptr& __root = __header._M_parent;
> >+
> >+      // Initialize fields in new node to insert.
> >+      __x->_M_parent = __p;
> >+      __x->_M_left = __x->_M_right = nullptr;
> >+      __x->_M_color = _S_red;
> >+
> >+      // Insert.
> >+      // Make new node child of parent and maintain root, leftmost and
> >+      // rightmost nodes.
> >+      // N.B. First node is always inserted left.
> >+      if (__insert_left)
> >+        {
> >+          __p->_M_left = __x; // also makes leftmost = __x when __p == &__header
> >+
> >+          if (std::__to_address(__p) == std::addressof(__header))
> >+            {
> >+              __header._M_parent = __x;
> >+              __header._M_right = __x;
> >+            }
> >+          else if (__p == __header._M_left)
> >+            __header._M_left = __x; // maintain leftmost pointing to min node
> >+        }
> >+      else
> >+        {
> >+          __p->_M_right = __x;
> >+
> >+          if (__p == __header._M_right)
> >+            __header._M_right = __x; // maintain rightmost pointing to max node
> >+        }
> >+      // Rebalance.
> >+      while (__x != __root
> >+             && __x->_M_parent->_M_color == _S_red)
> >+        {
> >+          const _Base_ptr __xpp = __x->_M_parent->_M_parent;
> >+
> >+          if (__x->_M_parent == __xpp->_M_left)
> >+            {
> >+              const _Base_ptr __y = __xpp->_M_right;
> >+              if (__y && __y->_M_color == _S_red)
> >+                {
> >+                  __x->_M_parent->_M_color = _S_black;
> >+                  __y->_M_color = _S_black;
> >+                  __xpp->_M_color = _S_red;
> >+                  __x = __xpp;
> >+                }
> >+              else
> >+                {
> >+                  if (__x == __x->_M_parent->_M_right)
> >+                    {
> >+                      __x = __x->_M_parent;
> >+                      _Rotate_left(__x, __root);
> >+                    }
> >+                  __x->_M_parent->_M_color = _S_black;
> >+                  __xpp->_M_color = _S_red;
> >+                  _Rotate_right(__xpp, __root);
> >+                }
> >+            }
> >+          else
> >+            {
> >+              const _Base_ptr __y = __xpp->_M_left;
> >+              if (__y && __y->_M_color == _S_red)
> >+                {
> >+                  __x->_M_parent->_M_color = _S_black;
> >+                  __y->_M_color = _S_black;
> >+                  __xpp->_M_color = _S_red;
> >+                  __x = __xpp;
> >+                }
> >+              else
> >+                {
> >+                  if (__x == __x->_M_parent->_M_left)
> >+                    {
> >+                      __x = __x->_M_parent;
> >+                      _Rotate_right(__x, __root);
> >+                    }
> >+                  __x->_M_parent->_M_color = _S_black;
> >+                  __xpp->_M_color = _S_red;
> >+                  _Rotate_left(__xpp, __root);
> >+                }
> >+            }
> >+        }
> >+      __root->_M_color = _S_black;
> >+      }
> >+
> >+      static _Base_ptr
> >+      _S_rebalance_for_erase(_Base_ptr __z, _Node_base& __header)
> >+      {
> >+      _Base_ptr& __root = __header._M_parent;
> >+      _Base_ptr& __leftmost = __header._M_left;
> >+      _Base_ptr& __rightmost = __header._M_right;
> >+      _Base_ptr __y = __z;
> >+      _Base_ptr __x{};
> >+      _Base_ptr __x_parent{};
> >+
> >+      if (!__y->_M_left)     // __z has at most one non-null child. y == z.
> >+        __x = __y->_M_right;     // __x might be null.
> >+      else
> >+        if (!__y->_M_right)  // __z has exactly one non-null child. y == z.
> >+          __x = __y->_M_left;    // __x is not null.
> >+        else
> >+          {
> >+            // __z has two non-null children.  Set __y to
> >+            __y = __y->_M_right;   //   __z's successor.  __x might be null.
> >+            while (__y->_M_left)
> >+              __y = __y->_M_left;
> >+            __x = __y->_M_right;
> >+          }
> >+      if (__y != __z)
> >+        {
> >+          // relink y in place of z.  y is z's successor
> >+          __z->_M_left->_M_parent = __y;
> >+          __y->_M_left = __z->_M_left;
> >+          if (__y != __z->_M_right)
> >+            {
> >+              __x_parent = __y->_M_parent;
> >+              if (__x)
> >+                __x->_M_parent = __y->_M_parent;
> >+              __y->_M_parent->_M_left = __x;   // __y must be a child of _M_left
> >+              __y->_M_right = __z->_M_right;
> >+              __z->_M_right->_M_parent = __y;
> >+            }
> >+          else
> >+            __x_parent = __y;
> >+          if (__root == __z)
> >+            __root = __y;
> >+          else if (__z->_M_parent->_M_left == __z)
> >+            __z->_M_parent->_M_left = __y;
> >+          else
> >+            __z->_M_parent->_M_right = __y;
> >+          __y->_M_parent = __z->_M_parent;
> >+          std::swap(__y->_M_color, __z->_M_color);
> >+          __y = __z;
> >+          // __y now points to node to be actually deleted
> >+        }
> >+      else
> >+        {                        // __y == __z
> >+          __x_parent = __y->_M_parent;
> >+          if (__x)
> >+            __x->_M_parent = __y->_M_parent;
> >+          if (__root == __z)
> >+            __root = __x;
> >+          else
> >+            if (__z->_M_parent->_M_left == __z)
> >+              __z->_M_parent->_M_left = __x;
> >+            else
> >+              __z->_M_parent->_M_right = __x;
> >+          if (__leftmost == __z)
> >+            {
> >+              if (!__z->_M_right)        // __z->_M_left must be null also
> >+                __leftmost = __z->_M_parent;
> >+              // makes __leftmost == _M_header if __z == __root
> >+              else
> >+                __leftmost = _Node_base::_S_minimum(__x);
> >+            }
> >+          if (__rightmost == __z)
> >+            {
> >+              if (__z->_M_left == 0)         // __z->_M_right must be null also
> >+                __rightmost = __z->_M_parent;
> >+              // makes __rightmost == _M_header if __z == __root
> >+              else                      // __x == __z->_M_left
> >+                __rightmost = _Node_base::_S_maximum(__x);
> >+            }
> >+        }
> >+      if (__y->_M_color != _S_red)
> >+        {
> >+          while (__x != __root && (__x == 0 || __x->_M_color == _S_black))
> >+            if (__x == __x_parent->_M_left)
> >+              {
> >+                _Base_ptr __w = __x_parent->_M_right;
> >+                if (__w->_M_color == _S_red)
> >+                  {
> >+                    __w->_M_color = _S_black;
> >+                    __x_parent->_M_color = _S_red;
> >+                    _Rotate_left(__x_parent, __root);
> >+                    __w = __x_parent->_M_right;
> >+                  }
> >+                if ((!__w->_M_left || __w->_M_left->_M_color == _S_black) &&
> >+                    (!__w->_M_right || __w->_M_right->_M_color == _S_black))
> >+                  {
> >+                    __w->_M_color = _S_red;
> >+                    __x = __x_parent;
> >+                    __x_parent = __x_parent->_M_parent;
> >+                  }
> >+                else
> >+                  {
> >+                    if (!__w->_M_right || __w->_M_right->_M_color == _S_black)
> >+                      {
> >+                        __w->_M_left->_M_color = _S_black;
> >+                        __w->_M_color = _S_red;
> >+                        _Rotate_right(__w, __root);
> >+                        __w = __x_parent->_M_right;
> >+                      }
> >+                    __w->_M_color = __x_parent->_M_color;
> >+                    __x_parent->_M_color = _S_black;
> >+                    if (__w->_M_right)
> >+                      __w->_M_right->_M_color = _S_black;
> >+                    _Rotate_left(__x_parent, __root);
> >+                    break;
> >+                  }
> >+              }
> >+            else
> >+              {
> >+                // same as above, with _M_right <-> _M_left.
> >+                _Base_ptr __w = __x_parent->_M_left;
> >+                if (__w->_M_color == _S_red)
> >+                  {
> >+                    __w->_M_color = _S_black;
> >+                    __x_parent->_M_color = _S_red;
> >+                    _Rotate_right(__x_parent, __root);
> >+                    __w = __x_parent->_M_left;
> >+                  }
> >+                if ((!__w->_M_right || __w->_M_right->_M_color == _S_black) &&
> >+                    (!__w->_M_left || __w->_M_left->_M_color == _S_black))
> >+                  {
> >+                    __w->_M_color = _S_red;
> >+                    __x = __x_parent;
> >+                    __x_parent = __x_parent->_M_parent;
> >+                  }
> >+                else
> >+                  {
> >+                    if (!__w->_M_left || __w->_M_left->_M_color == _S_black)
> >+                      {
> >+                        __w->_M_right->_M_color = _S_black;
> >+                        __w->_M_color = _S_red;
> >+                        _Rotate_left(__w, __root);
> >+                        __w = __x_parent->_M_left;
> >+                      }
> >+                    __w->_M_color = __x_parent->_M_color;
> >+                    __x_parent->_M_color = _S_black;
> >+                    if (__w->_M_left)
> >+                      __w->_M_left->_M_color = _S_black;
> >+                    _Rotate_right(__x_parent, __root);
> >+                    break;
> >+                  }
> >+              }
> >+          if (__x)
> >+            __x->_M_color = _S_black;
> >+        }
> >+
> >+      return __y;
> >+      }
> >+    };
> >+#endif
> >+} // namespace __rb_tree
> >+
> > #if __cplusplus > 201402L
> >   template<typename _Tree1, typename _Cmp2>
> >     struct _Rb_tree_merge_helper { };
> >@@ -425,15 +1025,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     class _Rb_tree
> >     {
> >       typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
> >-      rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
> >+      rebind<_Val>::other _Val_alloc_type;
> >+
> >+      typedef __gnu_cxx::__alloc_traits<_Val_alloc_type> _Val_alloc_traits;
> >+      typedef typename _Val_alloc_traits::pointer _ValPtr;
> >+      typedef __rb_tree::_Node_traits<_Val, _ValPtr> _Node_traits;
> >+
> >+      typedef typename _Node_traits::_Node_base               _Node_base;
> >+      typedef typename _Node_traits::_Node            _Node;
> >+
> >+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
> >+      rebind<_Node>::other _Node_allocator;
> >
> >-      typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
> >+      typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Node_alloc_traits;
> >
> >     protected:
> >-      typedef _Rb_tree_node_base*             _Base_ptr;
> >-      typedef const _Rb_tree_node_base*       _Const_Base_ptr;
> >-      typedef _Rb_tree_node<_Val>*            _Link_type;
> >-      typedef const _Rb_tree_node<_Val>*      _Const_Link_type;
> >+      typedef typename _Node_base::_Base_ptr          _Base_ptr;
> >+      typedef typename _Node_alloc_traits::pointer    _Node_ptr;
> >
> >     private:
> >       // Functor recycling a pool of nodes and using allocation once the pool
> >@@ -445,13 +1053,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       {
> >         if (_M_root)
> >           {
> >-            _M_root->_M_parent = 0;
> >+            _M_root->_M_parent = _Base_ptr();
> >
> >             if (_M_nodes->_M_left)
> >               _M_nodes = _M_nodes->_M_left;
> >           }
> >         else
> >-          _M_nodes = 0;
> >+          _M_nodes = _Base_ptr();
> >       }
> >
> > #if __cplusplus >= 201103L
> >@@ -459,13 +1067,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > #endif
> >
> >       ~_Reuse_or_alloc_node()
> >-      { _M_t._M_erase(static_cast<_Link_type>(_M_root)); }
> >+      { _M_t._M_erase(_M_root); }
> >
> >       template<typename _Arg>
> >-        _Link_type
> >+        _Base_ptr
>
> Changing this from a node pointer to a base pointer seems to weaken
> the API. We know it returns a pointer to a node, so can it return the
> correct type?
>
> >         operator()(_GLIBCXX_FWDREF(_Arg) __arg)
> >         {
> >-          _Link_type __node = static_cast<_Link_type>(_M_extract());
> >+          _Base_ptr __node = _M_extract();
> >           if (__node)
> >             {
> >               _M_t._M_destroy_node(__node);
> >@@ -489,7 +1097,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >           {
> >             if (_M_nodes->_M_right == __node)
> >               {
> >-                _M_nodes->_M_right = 0;
> >+                _M_nodes->_M_right = _Base_ptr();
> >
> >                 if (_M_nodes->_M_left)
> >                   {
> >@@ -503,10 +1111,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >                   }
> >               }
> >             else // __node is on the left.
> >-              _M_nodes->_M_left = 0;
> >+              _M_nodes->_M_left = _Base_ptr();
> >           }
> >         else
> >-          _M_root = 0;
> >+          _M_root = _Base_ptr();
> >
> >         return __node;
> >       }
> >@@ -524,9 +1132,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       : _M_t(__t) { }
> >
> >       template<typename _Arg>
> >-        _Link_type
> >+        _Base_ptr
>
> Same comment here.
>
> >         operator()(_GLIBCXX_FWDREF(_Arg) __arg) const
> >-        { return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg)); }
> >+        {
> >+          return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg));
> >+        }
> >
> >       private:
> >       _Rb_tree& _M_t;
> >@@ -556,18 +1166,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       { return allocator_type(_M_get_Node_allocator()); }
> >
> >     protected:
> >-      _Link_type
> >+      _Node_ptr
> >       _M_get_node()
> >-      { return _Alloc_traits::allocate(_M_get_Node_allocator(), 1); }
> >+      { return _Node_alloc_traits::allocate(_M_get_Node_allocator(), 1); }
> >
> >       void
> >-      _M_put_node(_Link_type __p) _GLIBCXX_NOEXCEPT
> >-      { _Alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1); }
> >+      _M_put_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
> >+      {
> >+      _Node_alloc_traits::deallocate
> >+        (_M_get_Node_allocator(), __p->template _M_node_ptr<_Node_ptr>(), 1);
> >+      }
> >
> > #if __cplusplus < 201103L
> >       void
> >-      _M_construct_node(_Link_type __node, const value_type& __x)
> >+      _M_construct_node(_Base_ptr __p, const value_type& __x)
>
> And can this be left as _Node_ptr instead of changed to _Base_ptr?
> Then it wouldn't need the static_cast, and the callers wouldn't need
> to use _M_base_ptr().
>
> Logically, it has to be a _Node_ptr or the cast isn't valid anyway.
>
> There seem to be several places like this where we're giving up
> valuable type information by using _base_ptr instead of _Node_ptr.
>
> Is that really necessary? If we have a _Node_ptr we can always get a
> _Base_ptr easily by calling _M_base_ptr().
>
> Is it a constness thing, because _M_begin() const was changed to
> return _Base_ptr instead of _Const_link_ptr?
>
>
> >       {
> >+      _Node_ptr __node = static_cast<_Node_ptr>(__p);
> >       __try
> >         { get_allocator().construct(__node->_M_valptr(), __x); }
> >       __catch(...)
> >@@ -577,79 +1191,83 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >         }
> >       }
> >
> >-      _Link_type
> >+      _Base_ptr
> >       _M_create_node(const value_type& __x)
> >       {
> >-      _Link_type __tmp = _M_get_node();
> >+      _Base_ptr __tmp = _M_get_node()->_M_base_ptr();
> >       _M_construct_node(__tmp, __x);
> >       return __tmp;
> >       }
> > #else
> >       template<typename... _Args>
> >       void
> >-      _M_construct_node(_Link_type __node, _Args&&... __args)
> >+      _M_construct_node(_Base_ptr __p, _Args&&... __args)
> >       {
> >+        _Node& __node = static_cast<_Node&>(*__p);
> >         __try
> >           {
> >-            ::new(__node) _Rb_tree_node<_Val>;
> >-            _Alloc_traits::construct(_M_get_Node_allocator(),
> >-                                     __node->_M_valptr(),
> >-                                     std::forward<_Args>(__args)...);
> >+            ::new(std::__addressof(__node)) _Node;
> >+            _Node_alloc_traits::construct(_M_get_Node_allocator(),
> >+                                          __node._M_valptr(),
> >+                                          std::forward<_Args>(__args)...);
> >           }
> >         __catch(...)
> >           {
> >-            __node->~_Rb_tree_node<_Val>();
> >-            _M_put_node(__node);
> >+            __node.~_Node();
> >+            _M_put_node(__p);
> >             __throw_exception_again;
> >           }
> >       }
> >
> >       template<typename... _Args>
> >-      _Link_type
> >+      _Base_ptr
> >       _M_create_node(_Args&&... __args)
> >       {
> >-        _Link_type __tmp = _M_get_node();
> >+        _Base_ptr __tmp = _M_get_node()->_M_base_ptr();
> >         _M_construct_node(__tmp, std::forward<_Args>(__args)...);
> >         return __tmp;
> >       }
> > #endif
> >
> >       void
> >-      _M_destroy_node(_Link_type __p) _GLIBCXX_NOEXCEPT
> >+      _M_destroy_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
> >       {
> > #if __cplusplus < 201103L
> >-      get_allocator().destroy(__p->_M_valptr());
> >+      get_allocator().destroy(static_cast<_Node_ptr>(__p)->_M_valptr());
> > #else
> >-      _Alloc_traits::destroy(_M_get_Node_allocator(), __p->_M_valptr());
> >-      __p->~_Rb_tree_node<_Val>();
> >+      _Node& __node = static_cast<_Node&>(*__p);
> >+      _Node_alloc_traits::destroy(_M_get_Node_allocator(),
> >+                                  __node._M_valptr());
> >+      __node.~_Node();
> > #endif
> >       }
> >
> >       void
> >-      _M_drop_node(_Link_type __p) _GLIBCXX_NOEXCEPT
> >+      _M_drop_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
> >       {
> >       _M_destroy_node(__p);
> >       _M_put_node(__p);
> >       }
> >
> >       template<bool _MoveValue, typename _NodeGen>
> >-      _Link_type
> >-      _M_clone_node(_Link_type __x, _NodeGen& __node_gen)
> >+      _Base_ptr
> >+      _M_clone_node(_Base_ptr __x, _NodeGen& __node_gen)
> >       {
> > #if __cplusplus >= 201103L
> >         using _Vp = __conditional_t<_MoveValue,
> >                                     value_type&&,
> >                                     const value_type&>;
> > #endif
> >-        _Link_type __tmp
> >-          = __node_gen(_GLIBCXX_FORWARD(_Vp, *__x->_M_valptr()));
> >+        _Base_ptr __tmp = __node_gen
> >+          (_GLIBCXX_FORWARD(_Vp, *static_cast<_Node&>(*__x)._M_valptr()));
> >         __tmp->_M_color = __x->_M_color;
> >-        __tmp->_M_left = 0;
> >-        __tmp->_M_right = 0;
> >+        __tmp->_M_left = __tmp->_M_right = _Base_ptr();
> >         return __tmp;
> >       }
> >
> >     protected:
> >+      typedef typename _Node_traits::_Header_t                _Header_t;
> >+
> > #if _GLIBCXX_INLINE_VERSION
> >       template<typename _Key_compare>
> > #else
> >@@ -660,7 +1278,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       struct _Rb_tree_impl
> >       : public _Node_allocator
> >       , public _Rb_tree_key_compare<_Key_compare>
> >-      , public _Rb_tree_header
> >+      , public _Header_t
> >       {
> >         typedef _Rb_tree_key_compare<_Key_compare> _Base_key_compare;
> >
> >@@ -672,9 +1290,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >         { }
> >
> >         _Rb_tree_impl(const _Rb_tree_impl& __x)
> >-        : _Node_allocator(_Alloc_traits::_S_select_on_copy(__x))
> >+        : _Node_allocator(_Node_alloc_traits::_S_select_on_copy(__x))
> >         , _Base_key_compare(__x._M_key_compare)
> >-        , _Rb_tree_header()
> >+        , _Header_t()
> >         { }
> >
> > #if __cplusplus < 201103L
> >@@ -694,7 +1312,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >         _Rb_tree_impl(_Rb_tree_impl&& __x, _Node_allocator&& __a)
> >         : _Node_allocator(std::move(__a)),
> >           _Base_key_compare(std::move(__x)),
> >-          _Rb_tree_header(std::move(__x))
> >+          _Header_t(std::move(__x))
> >         { }
> >
> >         _Rb_tree_impl(const _Key_compare& __comp, _Node_allocator&& __a)
> >@@ -710,7 +1328,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       _M_root() _GLIBCXX_NOEXCEPT
> >       { return this->_M_impl._M_header._M_parent; }
> >
> >-      _Const_Base_ptr
> >+      _Base_ptr
> >       _M_root() const _GLIBCXX_NOEXCEPT
> >       { return this->_M_impl._M_header._M_parent; }
> >
> >@@ -718,7 +1336,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       _M_leftmost() _GLIBCXX_NOEXCEPT
> >       { return this->_M_impl._M_header._M_left; }
> >
> >-      _Const_Base_ptr
> >+      _Base_ptr
> >       _M_leftmost() const _GLIBCXX_NOEXCEPT
> >       { return this->_M_impl._M_header._M_left; }
> >
> >@@ -726,35 +1344,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       _M_rightmost() _GLIBCXX_NOEXCEPT
> >       { return this->_M_impl._M_header._M_right; }
> >
> >-      _Const_Base_ptr
> >+      _Base_ptr
> >       _M_rightmost() const _GLIBCXX_NOEXCEPT
> >       { return this->_M_impl._M_header._M_right; }
> >
> >-      _Link_type
> >-      _M_mbegin() const _GLIBCXX_NOEXCEPT
> >-      { return static_cast<_Link_type>(this->_M_impl._M_header._M_parent); }
> >-
> >-      _Link_type
> >-      _M_begin() _GLIBCXX_NOEXCEPT
> >-      { return _M_mbegin(); }
> >-
> >-      _Const_Link_type
> >+      _Base_ptr
> >       _M_begin() const _GLIBCXX_NOEXCEPT
> >-      {
> >-      return static_cast<_Const_Link_type>
> >-        (this->_M_impl._M_header._M_parent);
> >-      }
> >+      { return this->_M_impl._M_header._M_parent; }
> >
> >       _Base_ptr
> >-      _M_end() _GLIBCXX_NOEXCEPT
> >-      { return &this->_M_impl._M_header; }
> >-
> >-      _Const_Base_ptr
> >       _M_end() const _GLIBCXX_NOEXCEPT
> >-      { return &this->_M_impl._M_header; }
> >+      { return this->_M_impl._M_header._M_base_ptr(); }
> >
> >       static const _Key&
> >-      _S_key(_Const_Link_type __x)
> >+      _S_key(_Base_ptr __x)
> >       {
> > #if __cplusplus >= 201103L
> >       // If we're asking for the key we're presumably using the comparison
> >@@ -772,48 +1375,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > # endif // C++17
> > #endif // C++11
> >
> >-      return _KeyOfValue()(*__x->_M_valptr());
> >+      return _KeyOfValue()(*static_cast<const _Node&>(*__x)._M_valptr());
> >       }
> >
> >-      static _Link_type
> >-      _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >-      { return static_cast<_Link_type>(__x->_M_left); }
> >-
> >-      static _Const_Link_type
> >-      _S_left(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >-      { return static_cast<_Const_Link_type>(__x->_M_left); }
> >-
> >-      static _Link_type
> >-      _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >-      { return static_cast<_Link_type>(__x->_M_right); }
> >-
> >-      static _Const_Link_type
> >-      _S_right(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >-      { return static_cast<_Const_Link_type>(__x->_M_right); }
> >-
> >-      static const _Key&
> >-      _S_key(_Const_Base_ptr __x)
> >-      { return _S_key(static_cast<_Const_Link_type>(__x)); }
> >-
> >       static _Base_ptr
> >-      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >-      { return _Rb_tree_node_base::_S_minimum(__x); }
> >-
> >-      static _Const_Base_ptr
> >-      _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >-      { return _Rb_tree_node_base::_S_minimum(__x); }
> >+      _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >+      { return __x->_M_left; }
> >
> >       static _Base_ptr
> >-      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >-      { return _Rb_tree_node_base::_S_maximum(__x); }
> >-
> >-      static _Const_Base_ptr
> >-      _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >-      { return _Rb_tree_node_base::_S_maximum(__x); }
> >+      _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
> >+      { return __x->_M_right; }
> >
> >     public:
> >-      typedef _Rb_tree_iterator<value_type>       iterator;
> >-      typedef _Rb_tree_const_iterator<value_type> const_iterator;
> >+      typedef typename _Node_traits::_Iterator                iterator;
> >+      typedef typename _Node_traits::_Const_iterator  const_iterator;
> >
> >       typedef std::reverse_iterator<iterator>       reverse_iterator;
> >       typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
> >@@ -846,7 +1421,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       _M_insert_(_Base_ptr __x, _Base_ptr __y, _Arg&& __v, _NodeGen&);
> >
> >       iterator
> >-      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Link_type __z);
> >+      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Base_ptr __z);
> >
> >       template<typename _Arg>
> >       iterator
> >@@ -857,10 +1432,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       _M_insert_equal_lower(_Arg&& __x);
> >
> >       iterator
> >-      _M_insert_lower_node(_Base_ptr __p, _Link_type __z);
> >+      _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z);
> >
> >       iterator
> >-      _M_insert_equal_lower_node(_Link_type __z);
> >+      _M_insert_equal_lower_node(_Base_ptr __z);
> > #else
> >       template<typename _NodeGen>
> >       iterator
> >@@ -879,22 +1454,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       enum { __as_lvalue, __as_rvalue };
> >
> >       template<bool _MoveValues, typename _NodeGen>
> >-      _Link_type
> >-      _M_copy(_Link_type, _Base_ptr, _NodeGen&);
> >+      _Base_ptr
> >+      _M_copy(_Base_ptr, _Base_ptr, _NodeGen&);
> >
> >       template<bool _MoveValues, typename _NodeGen>
> >-      _Link_type
> >+      _Base_ptr
> >       _M_copy(const _Rb_tree& __x, _NodeGen& __gen)
> >       {
> >-        _Link_type __root =
> >-          _M_copy<_MoveValues>(__x._M_mbegin(), _M_end(), __gen);
> >-        _M_leftmost() = _S_minimum(__root);
> >-        _M_rightmost() = _S_maximum(__root);
> >+        _Base_ptr __root =
> >+          _M_copy<_MoveValues>(__x._M_begin(), _M_end(), __gen);
> >+        _M_leftmost() = _Node_base::_S_minimum(__root);
> >+        _M_rightmost() = _Node_base::_S_maximum(__root);
> >         _M_impl._M_node_count = __x._M_impl._M_node_count;
> >         return __root;
> >       }
> >
> >-      _Link_type
> >+      _Base_ptr
> >       _M_copy(const _Rb_tree& __x)
> >       {
> >       _Alloc_node __an(*this);
> >@@ -902,22 +1477,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       }
> >
> >       void
> >-      _M_erase(_Link_type __x);
> >+      _M_erase(_Base_ptr __x);
> >
> >-      iterator
> >-      _M_lower_bound(_Link_type __x, _Base_ptr __y,
> >-                   const _Key& __k);
> >-
> >-      const_iterator
> >-      _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
> >+      _Base_ptr
> >+      _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
> >                    const _Key& __k) const;
> >
> >-      iterator
> >-      _M_upper_bound(_Link_type __x, _Base_ptr __y,
> >-                   const _Key& __k);
> >-
> >-      const_iterator
> >-      _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
> >+      _Base_ptr
> >+      _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
> >                    const _Key& __k) const;
> >
> >     public:
> >@@ -935,7 +1502,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       _Rb_tree(const _Rb_tree& __x)
> >       : _M_impl(__x._M_impl)
> >       {
> >-      if (__x._M_root() != 0)
> >+      if (__x._M_root())
> >         _M_root() = _M_copy(__x);
> >       }
> >
> >@@ -947,7 +1514,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       _Rb_tree(const _Rb_tree& __x, const allocator_type& __a)
> >       : _M_impl(__x._M_impl._M_key_compare, _Node_allocator(__a))
> >       {
> >-      if (__x._M_root() != nullptr)
> >+      if (__x._M_root())
> >         _M_root() = _M_copy(__x);
> >       }
> >
> >@@ -966,7 +1533,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a, false_type)
> >       : _M_impl(__x._M_impl._M_key_compare, std::move(__a))
> >       {
> >-      if (__x._M_root() != nullptr)
> >+      if (__x._M_root())
> >         _M_move_data(__x, false_type{});
> >       }
> >
> >@@ -974,9 +1541,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a)
> >       noexcept( noexcept(
> >       _Rb_tree(std::declval<_Rb_tree&&>(), std::declval<_Node_allocator&&>(),
> >-               std::declval<typename _Alloc_traits::is_always_equal>())) )
> >+               std::declval<typename _Node_alloc_traits::is_always_equal>())) )
> >       : _Rb_tree(std::move(__x), std::move(__a),
> >-               typename _Alloc_traits::is_always_equal{})
> >+               typename _Node_alloc_traits::is_always_equal{})
> >       { }
> > #endif
> >
> >@@ -1001,11 +1568,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> >       iterator
> >       end() _GLIBCXX_NOEXCEPT
> >-      { return iterator(&this->_M_impl._M_header); }
> >+      { return iterator(_M_end()); }
> >
> >       const_iterator
> >       end() const _GLIBCXX_NOEXCEPT
> >-      { return const_iterator(&this->_M_impl._M_header); }
> >+      { return const_iterator(_M_end()); }
> >
> >       reverse_iterator
> >       rbegin() _GLIBCXX_NOEXCEPT
> >@@ -1033,7 +1600,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> >       size_type
> >       max_size() const _GLIBCXX_NOEXCEPT
> >-      { return _Alloc_traits::max_size(_M_get_Node_allocator()); }
> >+      { return _Node_alloc_traits::max_size(_M_get_Node_allocator()); }
> >
> >       void
> >       swap(_Rb_tree& __t)
> >@@ -1194,7 +1761,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       const_iterator __result = __position;
> >       ++__result;
> >       _M_erase_aux(__position);
> >-      return __result._M_const_cast();
> >+      return iterator(__result._M_node);
> >       }
> >
> >       // LWG 2059.
> >@@ -1235,7 +1802,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       erase(const_iterator __first, const_iterator __last)
> >       {
> >       _M_erase_aux(__first, __last);
> >-      return __last._M_const_cast();
> >+      return iterator(__last._M_node);
> >       }
> > #else
> >       void
> >@@ -1266,19 +1833,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> >       iterator
> >       lower_bound(const key_type& __k)
> >-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
> >+      { return iterator(_M_lower_bound(_M_begin(), _M_end(), __k)); }
> >
> >       const_iterator
> >       lower_bound(const key_type& __k) const
> >-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
> >+      {
> >+      return const_iterator
> >+        (_M_lower_bound(_M_begin(), _M_end(), __k));
> >+      }
> >
> >       iterator
> >       upper_bound(const key_type& __k)
> >-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
> >+      { return iterator(_M_upper_bound(_M_begin(), _M_end(), __k)); }
> >
> >       const_iterator
> >       upper_bound(const key_type& __k) const
> >-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
> >+      {
> >+      return const_iterator
> >+        (_M_upper_bound(_M_begin(), _M_end(), __k));
> >+      }
> >
> >       pair<iterator, iterator>
> >       equal_range(const key_type& __k);
> >@@ -1293,7 +1866,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       _M_find_tr(const _Kt& __k)
> >       {
> >         const _Rb_tree* __const_this = this;
> >-        return __const_this->_M_find_tr(__k)._M_const_cast();
> >+        return iterator(__const_this->_M_find_tr(__k)._M_node);
> >       }
> >
> >       template<typename _Kt,
> >@@ -1301,7 +1874,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       const_iterator
> >       _M_find_tr(const _Kt& __k) const
> >       {
> >-        auto __j = _M_lower_bound_tr(__k);
> >+        const_iterator __j(_M_lower_bound_tr(__k));
> >         if (__j != end() && _M_impl._M_key_compare(__k, _S_key(__j._M_node)))
> >           __j = end();
> >         return __j;
> >@@ -1318,21 +1891,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> >       template<typename _Kt,
> >              typename _Req = __has_is_transparent_t<_Compare, _Kt>>
> >-      iterator
> >-      _M_lower_bound_tr(const _Kt& __k)
> >-      {
> >-        const _Rb_tree* __const_this = this;
> >-        return __const_this->_M_lower_bound_tr(__k)._M_const_cast();
> >-      }
> >-
> >-      template<typename _Kt,
> >-             typename _Req = __has_is_transparent_t<_Compare, _Kt>>
> >-      const_iterator
> >+      _Base_ptr
> >       _M_lower_bound_tr(const _Kt& __k) const
> >       {
> >         auto __x = _M_begin();
> >         auto __y = _M_end();
> >-        while (__x != 0)
> >+        while (__x)
> >           if (!_M_impl._M_key_compare(_S_key(__x), __k))
> >             {
> >               __y = __x;
> >@@ -1340,26 +1904,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >             }
> >           else
> >             __x = _S_right(__x);
> >-        return const_iterator(__y);
> >+        return __y;
> >       }
> >
> >       template<typename _Kt,
> >              typename _Req = __has_is_transparent_t<_Compare, _Kt>>
> >-      iterator
> >-      _M_upper_bound_tr(const _Kt& __k)
> >-      {
> >-        const _Rb_tree* __const_this = this;
> >-        return __const_this->_M_upper_bound_tr(__k)._M_const_cast();
> >-      }
> >-
> >-      template<typename _Kt,
> >-             typename _Req = __has_is_transparent_t<_Compare, _Kt>>
> >-      const_iterator
> >+      _Base_ptr
> >       _M_upper_bound_tr(const _Kt& __k) const
> >       {
> >         auto __x = _M_begin();
> >         auto __y = _M_end();
> >-        while (__x != 0)
> >+        while (__x)
> >           if (_M_impl._M_key_compare(__k, _S_key(__x)))
> >             {
> >               __y = __x;
> >@@ -1367,7 +1922,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >             }
> >           else
> >             __x = _S_right(__x);
> >-        return const_iterator(__y);
> >+        return __y;
> >       }
> >
> >       template<typename _Kt,
> >@@ -1377,7 +1932,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       {
> >         const _Rb_tree* __const_this = this;
> >         auto __ret = __const_this->_M_equal_range_tr(__k);
> >-        return { __ret.first._M_const_cast(), __ret.second._M_const_cast() };
> >+        return
> >+          { iterator(__ret.first._M_node), iterator(__ret.second._M_node) };
> >       }
> >
> >       template<typename _Kt,
> >@@ -1385,7 +1941,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       pair<const_iterator, const_iterator>
> >       _M_equal_range_tr(const _Kt& __k) const
> >       {
> >-        auto __low = _M_lower_bound_tr(__k);
> >+        const_iterator __low(_M_lower_bound_tr(__k));
> >         auto __high = __low;
> >         auto& __cmp = _M_impl._M_key_compare;
> >         while (__high != end() && !__cmp(__k, _S_key(__high._M_node)))
> >@@ -1401,7 +1957,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > #if __cplusplus >= 201103L
> >       _Rb_tree&
> >       operator=(_Rb_tree&&)
> >-      noexcept(_Alloc_traits::_S_nothrow_move()
> >+      noexcept(_Node_alloc_traits::_S_nothrow_move()
> >              && is_nothrow_move_assignable<_Compare>::value);
> >
> >       template<typename _Iterator>
> >@@ -1449,8 +2005,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >           auto __res = _M_get_insert_unique_pos(__nh._M_key());
> >           if (__res.second)
> >             {
> >-              __ret.position
> >-                = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
> >+              __ret.position = _M_insert_node(__res.first, __res.second,
> >+                                              __nh._M_ptr->_M_base_ptr());
> >               __nh.release();
> >               __ret.inserted = true;
> >             }
> >@@ -1476,9 +2032,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >           __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
> >           auto __res = _M_get_insert_equal_pos(__nh._M_key());
> >           if (__res.second)
> >-            __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
> >+            __ret = _M_insert_node(__res.first, __res.second,
> >+                                   __nh._M_ptr->_M_base_ptr());
> >           else
> >-            __ret = _M_insert_equal_lower_node(__nh._M_ptr);
> >+            __ret = _M_insert_equal_lower_node(__nh._M_ptr->_M_base_ptr());
> >           __nh.release();
> >         }
> >       return __ret;
> >@@ -1497,7 +2054,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >           auto __res = _M_get_insert_hint_unique_pos(__hint, __nh._M_key());
> >           if (__res.second)
> >             {
> >-              __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
> >+              __ret = _M_insert_node(__res.first, __res.second,
> >+                                     __nh._M_ptr->_M_base_ptr());
> >               __nh.release();
> >             }
> >           else
> >@@ -1518,9 +2076,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >           __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
> >           auto __res = _M_get_insert_hint_equal_pos(__hint, __nh._M_key());
> >           if (__res.second)
> >-            __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
> >+            __ret = _M_insert_node(__res.first, __res.second,
> >+                                   __nh._M_ptr->_M_base_ptr());
> >           else
> >-            __ret = _M_insert_equal_lower_node(__nh._M_ptr);
> >+            __ret = _M_insert_equal_lower_node(__nh._M_ptr->_M_base_ptr());
> >           __nh.release();
> >         }
> >       return __ret;
> >@@ -1530,10 +2089,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       node_type
> >       extract(const_iterator __pos)
> >       {
> >-      auto __ptr = _Rb_tree_rebalance_for_erase(
> >-          __pos._M_const_cast()._M_node, _M_impl._M_header);
> >+      auto __ptr = _Node_traits::_S_rebalance_for_erase
> >+        (__pos._M_node, _M_impl._M_header);
> >       --_M_impl._M_node_count;
> >-      return { static_cast<_Link_type>(__ptr), _M_get_Node_allocator() };
> >+      return
> >+        { __ptr->template _M_node_ptr<_Node_ptr>(), _M_get_Node_allocator() };
> >       }
> >
> >       /// Extract a node.
> >@@ -1567,11 +2127,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >             if (__res.second)
> >               {
> >                 auto& __src_impl = _Merge_helper::_S_get_impl(__src);
> >-                auto __ptr = _Rb_tree_rebalance_for_erase(
> >-                    __pos._M_node, __src_impl._M_header);
> >+                auto __ptr = _Node_traits::_S_rebalance_for_erase
> >+                  (__pos._M_node, __src_impl._M_header);
> >                 --__src_impl._M_node_count;
> >-                _M_insert_node(__res.first, __res.second,
> >-                               static_cast<_Link_type>(__ptr));
> >+                _M_insert_node(__res.first, __res.second, __ptr);
> >               }
> >           }
> >       }
> >@@ -1589,11 +2148,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >             if (__res.second)
> >               {
> >                 auto& __src_impl = _Merge_helper::_S_get_impl(__src);
> >-                auto __ptr = _Rb_tree_rebalance_for_erase(
> >-                    __pos._M_node, __src_impl._M_header);
> >+                auto __ptr = _Node_traits::_S_rebalance_for_erase
> >+                  (__pos._M_node, __src_impl._M_header);
> >                 --__src_impl._M_node_count;
> >-                _M_insert_node(__res.first, __res.second,
> >-                               static_cast<_Link_type>(__ptr));
> >+                _M_insert_node(__res.first, __res.second, __ptr);
> >               }
> >           }
> >       }
> >@@ -1666,7 +2224,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       }
> >
> >       _Rb_tree& _M_t;
> >-      _Link_type _M_node;
> >+      _Base_ptr _M_node;
> >       };
> > #endif // C++11
> >     };
> >@@ -1704,7 +2262,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     _M_move_assign(_Rb_tree& __x, true_type)
> >     {
> >       clear();
> >-      if (__x._M_root() != nullptr)
> >+      if (__x._M_root())
> >       _M_move_data(__x, true_type());
> >       std::__alloc_on_move(_M_get_Node_allocator(),
> >                          __x._M_get_Node_allocator());
> >@@ -1723,7 +2281,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       // structure.
> >       _Reuse_or_alloc_node __roan(*this);
> >       _M_impl._M_reset();
> >-      if (__x._M_root() != nullptr)
> >+      if (__x._M_root())
> >       {
> >         _M_root() = _M_copy<__as_rvalue>(__x, __roan);
> >         __x.clear();
> >@@ -1735,11 +2293,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     inline _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >     operator=(_Rb_tree&& __x)
> >-    noexcept(_Alloc_traits::_S_nothrow_move()
> >+    noexcept(_Node_alloc_traits::_S_nothrow_move()
> >            && is_nothrow_move_assignable<_Compare>::value)
> >     {
> >       _M_impl._M_key_compare = std::move(__x._M_impl._M_key_compare);
> >-      _M_move_assign(__x, __bool_constant<_Alloc_traits::_S_nothrow_move()>());
> >+      _M_move_assign(__x,
> >+                   __bool_constant<_Node_alloc_traits::_S_nothrow_move()>());
> >       return *this;
> >     }
> >
> >@@ -1780,11 +2339,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       {
> >         // Note that _Key may be a constant type.
> > #if __cplusplus >= 201103L
> >-        if (_Alloc_traits::_S_propagate_on_copy_assign())
> >+        if (_Node_alloc_traits::_S_propagate_on_copy_assign())
> >           {
> >             auto& __this_alloc = this->_M_get_Node_allocator();
> >             auto& __that_alloc = __x._M_get_Node_allocator();
> >-            if (!_Alloc_traits::_S_always_equal()
> >+            if (!_Node_alloc_traits::_S_always_equal()
> >                 && __this_alloc != __that_alloc)
> >               {
> >                 // Replacement allocator cannot free existing storage, we need
> >@@ -1798,7 +2357,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >         _Reuse_or_alloc_node __roan(*this);
> >         _M_impl._M_reset();
> >         _M_impl._M_key_compare = __x._M_impl._M_key_compare;
> >-        if (__x._M_root() != 0)
> >+        if (__x._M_root())
> >           _M_root() = _M_copy<__as_lvalue>(__x, __roan);
> >       }
> >
> >@@ -1822,14 +2381,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > #endif
> >                _NodeGen& __node_gen)
> >       {
> >-      bool __insert_left = (__x != 0 || __p == _M_end()
> >+      bool __insert_left = (__x || __p == _M_end()
> >                             || _M_impl._M_key_compare(_KeyOfValue()(__v),
> >                                                       _S_key(__p)));
> >
> >-      _Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
> >+      _Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
> >
> >-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
> >-                                    this->_M_impl._M_header);
> >+      _Node_traits::_S_insert_and_rebalance
> >+        (__insert_left, __z, __p, this->_M_impl._M_header);
> >       ++_M_impl._M_node_count;
> >       return iterator(__z);
> >       }
> >@@ -1851,10 +2410,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >                           || !_M_impl._M_key_compare(_S_key(__p),
> >                                                      _KeyOfValue()(__v)));
> >
> >-      _Link_type __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
> >-
> >-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
> >-                                  this->_M_impl._M_header);
> >+      _Base_ptr __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
> >+      _Node_traits::_S_insert_and_rebalance
> >+      (__insert_left, __z, __p, this->_M_impl._M_header);
> >       ++_M_impl._M_node_count;
> >       return iterator(__z);
> >     }
> >@@ -1872,9 +2430,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     _M_insert_equal_lower(const _Val& __v)
> > #endif
> >     {
> >-      _Link_type __x = _M_begin();
> >+      _Base_ptr __x = _M_begin();
> >       _Base_ptr __y = _M_end();
> >-      while (__x != 0)
> >+      while (__x)
> >       {
> >         __y = __x;
> >         __x = !_M_impl._M_key_compare(_S_key(__x), _KeyOfValue()(__v)) ?
> >@@ -1886,12 +2444,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >   template<typename _Key, typename _Val, typename _KoV,
> >          typename _Compare, typename _Alloc>
> >     template<bool _MoveValues, typename _NodeGen>
> >-      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Link_type
> >+      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Base_ptr
> >       _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::
> >-      _M_copy(_Link_type __x, _Base_ptr __p, _NodeGen& __node_gen)
> >+      _M_copy(_Base_ptr __x, _Base_ptr __p, _NodeGen& __node_gen)
> >       {
> >       // Structural copy. __x and __p must be non-null.
> >-      _Link_type __top = _M_clone_node<_MoveValues>(__x, __node_gen);
> >+      _Base_ptr __top = _M_clone_node<_MoveValues>(__x, __node_gen);
> >       __top->_M_parent = __p;
> >
> >       __try
> >@@ -1902,9 +2460,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >           __p = __top;
> >           __x = _S_left(__x);
> >
> >-          while (__x != 0)
> >+          while (__x)
> >             {
> >-              _Link_type __y = _M_clone_node<_MoveValues>(__x, __node_gen);
> >+              _Base_ptr __y = _M_clone_node<_MoveValues>(__x, __node_gen);
> >               __p->_M_left = __y;
> >               __y->_M_parent = __p;
> >               if (__x->_M_right)
> >@@ -1926,13 +2484,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >          typename _Compare, typename _Alloc>
> >     void
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >-    _M_erase(_Link_type __x)
> >+    _M_erase(_Base_ptr __x)
> >     {
> >       // Erase without rebalancing.
> >-      while (__x != 0)
> >+      while (__x)
> >       {
> >         _M_erase(_S_right(__x));
> >-        _Link_type __y = _S_left(__x);
> >+        _Base_ptr __y = _S_left(__x);
> >         _M_drop_node(__x);
> >         __x = __y;
> >       }
> >@@ -1941,65 +2499,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >   template<typename _Key, typename _Val, typename _KeyOfValue,
> >          typename _Compare, typename _Alloc>
> >     typename _Rb_tree<_Key, _Val, _KeyOfValue,
> >-                    _Compare, _Alloc>::iterator
> >+                    _Compare, _Alloc>::_Base_ptr
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >-    _M_lower_bound(_Link_type __x, _Base_ptr __y,
> >-                 const _Key& __k)
> >-    {
> >-      while (__x != 0)
> >-      if (!_M_impl._M_key_compare(_S_key(__x), __k))
> >-        __y = __x, __x = _S_left(__x);
> >-      else
> >-        __x = _S_right(__x);
> >-      return iterator(__y);
> >-    }
> >-
> >-  template<typename _Key, typename _Val, typename _KeyOfValue,
> >-         typename _Compare, typename _Alloc>
> >-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
> >-                    _Compare, _Alloc>::const_iterator
> >-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >-    _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
> >+    _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
> >                  const _Key& __k) const
> >     {
> >-      while (__x != 0)
> >+      while (__x)
> >       if (!_M_impl._M_key_compare(_S_key(__x), __k))
> >         __y = __x, __x = _S_left(__x);
> >       else
> >         __x = _S_right(__x);
> >-      return const_iterator(__y);
> >-    }
> >-
> >-  template<typename _Key, typename _Val, typename _KeyOfValue,
> >-         typename _Compare, typename _Alloc>
> >-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
> >-                    _Compare, _Alloc>::iterator
> >-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >-    _M_upper_bound(_Link_type __x, _Base_ptr __y,
> >-                 const _Key& __k)
> >-    {
> >-      while (__x != 0)
> >-      if (_M_impl._M_key_compare(__k, _S_key(__x)))
> >-        __y = __x, __x = _S_left(__x);
> >-      else
> >-        __x = _S_right(__x);
> >-      return iterator(__y);
> >+      return __y;
> >     }
> >
> >   template<typename _Key, typename _Val, typename _KeyOfValue,
> >          typename _Compare, typename _Alloc>
> >     typename _Rb_tree<_Key, _Val, _KeyOfValue,
> >-                    _Compare, _Alloc>::const_iterator
> >+                    _Compare, _Alloc>::_Base_ptr
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >-    _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
> >+    _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
> >                  const _Key& __k) const
> >     {
> >-      while (__x != 0)
> >+      while (__x)
> >       if (_M_impl._M_key_compare(__k, _S_key(__x)))
> >         __y = __x, __x = _S_left(__x);
> >       else
> >         __x = _S_right(__x);
> >-      return const_iterator(__y);
> >+      return __y;
> >     }
> >
> >   template<typename _Key, typename _Val, typename _KeyOfValue,
> >@@ -2011,9 +2537,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >     equal_range(const _Key& __k)
> >     {
> >-      _Link_type __x = _M_begin();
> >+      _Base_ptr __x = _M_begin();
> >       _Base_ptr __y = _M_end();
> >-      while (__x != 0)
> >+      while (__x)
> >       {
> >         if (_M_impl._M_key_compare(_S_key(__x), __k))
> >           __x = _S_right(__x);
> >@@ -2021,13 +2547,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >           __y = __x, __x = _S_left(__x);
> >         else
> >           {
> >-            _Link_type __xu(__x);
> >+            _Base_ptr __xu(__x);
> >             _Base_ptr __yu(__y);
> >             __y = __x, __x = _S_left(__x);
> >             __xu = _S_right(__xu);
> >-            return pair<iterator,
> >-                        iterator>(_M_lower_bound(__x, __y, __k),
> >-                                  _M_upper_bound(__xu, __yu, __k));
> >+            return make_pair(iterator(_M_lower_bound(__x, __y, __k)),
> >+                             iterator(_M_upper_bound(__xu, __yu, __k)));
> >           }
> >       }
> >       return pair<iterator, iterator>(iterator(__y),
> >@@ -2043,9 +2568,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >     equal_range(const _Key& __k) const
> >     {
> >-      _Const_Link_type __x = _M_begin();
> >-      _Const_Base_ptr __y = _M_end();
> >-      while (__x != 0)
> >+      _Base_ptr __x = _M_begin();
> >+      _Base_ptr __y = _M_end();
> >+      while (__x)
> >       {
> >         if (_M_impl._M_key_compare(_S_key(__x), __k))
> >           __x = _S_right(__x);
> >@@ -2053,13 +2578,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >           __y = __x, __x = _S_left(__x);
> >         else
> >           {
> >-            _Const_Link_type __xu(__x);
> >-            _Const_Base_ptr __yu(__y);
> >+            _Base_ptr __xu(__x);
> >+            _Base_ptr __yu(__y);
> >             __y = __x, __x = _S_left(__x);
> >             __xu = _S_right(__xu);
> >-            return pair<const_iterator,
> >-                        const_iterator>(_M_lower_bound(__x, __y, __k),
> >-                                        _M_upper_bound(__xu, __yu, __k));
> >+            return make_pair(const_iterator(_M_lower_bound(__x, __y, __k)),
> >+                             const_iterator(_M_upper_bound(__xu, __yu, __k)));
> >           }
> >       }
> >       return pair<const_iterator, const_iterator>(const_iterator(__y),
> >@@ -2073,12 +2597,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     swap(_Rb_tree& __t)
> >     _GLIBCXX_NOEXCEPT_IF(__is_nothrow_swappable<_Compare>::value)
> >     {
> >-      if (_M_root() == 0)
> >+      if (!_M_root())
> >       {
> >-        if (__t._M_root() != 0)
> >+        if (__t._M_root())
> >           _M_impl._M_move_data(__t._M_impl);
> >       }
> >-      else if (__t._M_root() == 0)
> >+      else if (!__t._M_root())
> >       __t._M_impl._M_move_data(_M_impl);
> >       else
> >       {
> >@@ -2093,8 +2617,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       // No need to swap header's color as it does not change.
> >       std::swap(this->_M_impl._M_key_compare, __t._M_impl._M_key_compare);
> >
> >-      _Alloc_traits::_S_on_swap(_M_get_Node_allocator(),
> >-                              __t._M_get_Node_allocator());
> >+      _Node_alloc_traits::_S_on_swap(_M_get_Node_allocator(),
> >+                                   __t._M_get_Node_allocator());
> >     }
> >
> >   template<typename _Key, typename _Val, typename _KeyOfValue,
> >@@ -2107,10 +2631,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     _M_get_insert_unique_pos(const key_type& __k)
> >     {
> >       typedef pair<_Base_ptr, _Base_ptr> _Res;
> >-      _Link_type __x = _M_begin();
> >+      _Base_ptr __x = _M_begin();
> >       _Base_ptr __y = _M_end();
> >       bool __comp = true;
> >-      while (__x != 0)
> >+      while (__x)
> >       {
> >         __y = __x;
> >         __comp = _M_impl._M_key_compare(__k, _S_key(__x));
> >@@ -2126,7 +2650,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       }
> >       if (_M_impl._M_key_compare(_S_key(__j._M_node), __k))
> >       return _Res(__x, __y);
> >-      return _Res(__j._M_node, 0);
> >+      return _Res(__j._M_node, _Base_ptr());
> >     }
> >
> >   template<typename _Key, typename _Val, typename _KeyOfValue,
> >@@ -2139,9 +2663,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     _M_get_insert_equal_pos(const key_type& __k)
> >     {
> >       typedef pair<_Base_ptr, _Base_ptr> _Res;
> >-      _Link_type __x = _M_begin();
> >+      _Base_ptr __x = _M_begin();
> >       _Base_ptr __y = _M_end();
> >-      while (__x != 0)
> >+      while (__x)
> >       {
> >         __y = __x;
> >         __x = _M_impl._M_key_compare(__k, _S_key(__x)) ?
> >@@ -2209,44 +2733,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     _M_get_insert_hint_unique_pos(const_iterator __position,
> >                                 const key_type& __k)
> >     {
> >-      iterator __pos = __position._M_const_cast();
> >       typedef pair<_Base_ptr, _Base_ptr> _Res;
> >
> >       // end()
> >-      if (__pos._M_node == _M_end())
> >+      if (__position._M_node == _M_end())
> >       {
> >         if (size() > 0
> >             && _M_impl._M_key_compare(_S_key(_M_rightmost()), __k))
> >-          return _Res(0, _M_rightmost());
> >+          return _Res(_Base_ptr(), _M_rightmost());
> >         else
> >           return _M_get_insert_unique_pos(__k);
> >       }
> >-      else if (_M_impl._M_key_compare(__k, _S_key(__pos._M_node)))
> >+      else if (_M_impl._M_key_compare(__k, _S_key(__position._M_node)))
> >       {
> >         // First, try before...
> >-        iterator __before = __pos;
> >-        if (__pos._M_node == _M_leftmost()) // begin()
> >+        iterator __before(__position._M_node);
> >+        if (__position._M_node == _M_leftmost()) // begin()
> >           return _Res(_M_leftmost(), _M_leftmost());
> >         else if (_M_impl._M_key_compare(_S_key((--__before)._M_node), __k))
> >           {
> >-            if (_S_right(__before._M_node) == 0)
> >-              return _Res(0, __before._M_node);
> >+            if (!_S_right(__before._M_node))
> >+              return _Res(_Base_ptr(), __before._M_node);
> >             else
> >-              return _Res(__pos._M_node, __pos._M_node);
> >+              return _Res(__position._M_node, __position._M_node);
> >           }
> >         else
> >           return _M_get_insert_unique_pos(__k);
> >       }
> >-      else if (_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
> >+      else if (_M_impl._M_key_compare(_S_key(__position._M_node), __k))
> >       {
> >         // ... then try after.
> >-        iterator __after = __pos;
> >-        if (__pos._M_node == _M_rightmost())
> >-          return _Res(0, _M_rightmost());
> >+        iterator __after(__position._M_node);
> >+        if (__position._M_node == _M_rightmost())
> >+          return _Res(_Base_ptr(), _M_rightmost());
> >         else if (_M_impl._M_key_compare(__k, _S_key((++__after)._M_node)))
> >           {
> >-            if (_S_right(__pos._M_node) == 0)
> >-              return _Res(0, __pos._M_node);
> >+            if (!_S_right(__position._M_node))
> >+              return _Res(_Base_ptr(), __position._M_node);
> >             else
> >               return _Res(__after._M_node, __after._M_node);
> >           }
> >@@ -2255,7 +2778,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       }
> >       else
> >       // Equivalent keys.
> >-      return _Res(__pos._M_node, 0);
> >+      return _Res(__position._M_node, _Base_ptr());
> >     }
> >
> >   template<typename _Key, typename _Val, typename _KeyOfValue,
> >@@ -2294,30 +2817,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >     _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
> >     {
> >-      iterator __pos = __position._M_const_cast();
> >       typedef pair<_Base_ptr, _Base_ptr> _Res;
> >
> >       // end()
> >-      if (__pos._M_node == _M_end())
> >+      if (__position._M_node == _M_end())
> >       {
> >         if (size() > 0
> >             && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
> >-          return _Res(0, _M_rightmost());
> >+          return _Res(_Base_ptr(), _M_rightmost());
> >         else
> >           return _M_get_insert_equal_pos(__k);
> >       }
> >-      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
> >+      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
> >       {
> >         // First, try before...
> >-        iterator __before = __pos;
> >-        if (__pos._M_node == _M_leftmost()) // begin()
> >+        iterator __before(__position._M_node);
> >+        if (__position._M_node == _M_leftmost()) // begin()
> >           return _Res(_M_leftmost(), _M_leftmost());
> >         else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
> >           {
> >-            if (_S_right(__before._M_node) == 0)
> >-              return _Res(0, __before._M_node);
> >+            if (!_S_right(__before._M_node))
> >+              return _Res(_Base_ptr(), __before._M_node);
> >             else
> >-              return _Res(__pos._M_node, __pos._M_node);
> >+              return _Res(__position._M_node, __position._M_node);
> >           }
> >         else
> >           return _M_get_insert_equal_pos(__k);
> >@@ -2325,18 +2847,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       else
> >       {
> >         // ... then try after.
> >-        iterator __after = __pos;
> >-        if (__pos._M_node == _M_rightmost())
> >-          return _Res(0, _M_rightmost());
> >+        iterator __after(__position._M_node);
> >+        if (__position._M_node == _M_rightmost())
> >+          return _Res(_Base_ptr(), _M_rightmost());
> >         else if (!_M_impl._M_key_compare(_S_key((++__after)._M_node), __k))
> >           {
> >-            if (_S_right(__pos._M_node) == 0)
> >-              return _Res(0, __pos._M_node);
> >+            if (!_S_right(__position._M_node))
> >+              return _Res(_Base_ptr(), __position._M_node);
> >             else
> >               return _Res(__after._M_node, __after._M_node);
> >           }
> >         else
> >-          return _Res(0, 0);
> >+          return _Res(_Base_ptr(), _Base_ptr());
> >       }
> >     }
> >
> >@@ -2373,15 +2895,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >          typename _Compare, typename _Alloc>
> >     auto
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >-    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Link_type __z)
> >+    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Base_ptr __z)
> >     -> iterator
> >     {
> >-      bool __insert_left = (__x != 0 || __p == _M_end()
> >+      bool __insert_left = (__x || __p == _M_end()
> >                           || _M_impl._M_key_compare(_S_key(__z),
> >                                                     _S_key(__p)));
> >
> >-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
> >-                                  this->_M_impl._M_header);
> >+      _Node_traits::_S_insert_and_rebalance
> >+      (__insert_left, __z, __p, this->_M_impl._M_header);
> >       ++_M_impl._M_node_count;
> >       return iterator(__z);
> >     }
> >@@ -2390,15 +2912,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >          typename _Compare, typename _Alloc>
> >     auto
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >-    _M_insert_lower_node(_Base_ptr __p, _Link_type __z)
> >+    _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z)
> >     -> iterator
> >     {
> >       bool __insert_left = (__p == _M_end()
> >                           || !_M_impl._M_key_compare(_S_key(__p),
> >                                                      _S_key(__z)));
> >
> >-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
> >-                                  this->_M_impl._M_header);
> >+      _Node_traits::_S_insert_and_rebalance
> >+      (__insert_left, __z, __p, this->_M_impl._M_header);
> >       ++_M_impl._M_node_count;
> >       return iterator(__z);
> >     }
> >@@ -2407,12 +2929,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >          typename _Compare, typename _Alloc>
> >     auto
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >-    _M_insert_equal_lower_node(_Link_type __z)
> >+    _M_insert_equal_lower_node(_Base_ptr __z)
> >     -> iterator
> >     {
> >-      _Link_type __x = _M_begin();
> >+      _Base_ptr __x = _M_begin();
> >       _Base_ptr __y = _M_end();
> >-      while (__x != 0)
> >+      while (__x)
> >       {
> >         __y = __x;
> >         __x = !_M_impl._M_key_compare(_S_key(__x), _S_key(__z)) ?
> >@@ -2487,10 +3009,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >     _M_erase_aux(const_iterator __position)
> >     {
> >-      _Link_type __y =
> >-      static_cast<_Link_type>(_Rb_tree_rebalance_for_erase
> >-                              (const_cast<_Base_ptr>(__position._M_node),
> >-                               this->_M_impl._M_header));
> >+      _Base_ptr __y = _Node_traits::_S_rebalance_for_erase
> >+      (__position._M_node, this->_M_impl._M_header);
> >       _M_drop_node(__y);
> >       --_M_impl._M_node_count;
> >     }
> >@@ -2527,7 +3047,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >     find(const _Key& __k)
> >     {
> >-      iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
> >+      iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
> >       return (__j == end()
> >             || _M_impl._M_key_compare(__k,
> >                                       _S_key(__j._M_node))) ? end() : __j;
> >@@ -2540,7 +3060,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
> >     find(const _Key& __k) const
> >     {
> >-      const_iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
> >+      const_iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
> >       return (__j == end()
> >             || _M_impl._M_key_compare(__k,
> >                                       _S_key(__j._M_node))) ? end() : __j;
> >@@ -2574,9 +3094,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       unsigned int __len = _Rb_tree_black_count(_M_leftmost(), _M_root());
> >       for (const_iterator __it = begin(); __it != end(); ++__it)
> >       {
> >-        _Const_Link_type __x = static_cast<_Const_Link_type>(__it._M_node);
> >-        _Const_Link_type __L = _S_left(__x);
> >-        _Const_Link_type __R = _S_right(__x);
> >+        _Base_ptr __x = __it._M_node;
> >+        _Base_ptr __L = _S_left(__x);
> >+        _Base_ptr __R = _S_right(__x);
> >
> >         if (__x->_M_color == _S_red)
> >           if ((__L && __L->_M_color == _S_red)
> >@@ -2592,9 +3112,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >           return false;
> >       }
> >
> >-      if (_M_leftmost() != _Rb_tree_node_base::_S_minimum(_M_root()))
> >+      if (_M_leftmost() != _Node_base::_S_minimum(_M_root()))
> >       return false;
> >-      if (_M_rightmost() != _Rb_tree_node_base::_S_maximum(_M_root()))
> >+      if (_M_rightmost() != _Node_base::_S_maximum(_M_root()))
> >       return false;
> >       return true;
> >     }
> >diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
> >new file mode 100644
> >index 00000000000..3d83738f86b
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
> >@@ -0,0 +1,37 @@
> >+// { dg-do run { target c++11 } }
> >+
> >+#include <map>
> >+#include <memory>
> >+#include <testsuite_hooks.h>
> >+#include <testsuite_allocator.h>
> >+
> >+struct T { int i; };
> >+
> >+bool operator<(const T& l, const T& r)
> >+{ return l.i < r.i; }
> >+
> >+struct L : std::less<T>
> >+{ };
> >+
> >+using __gnu_test::CustomPointerAlloc;
> >+
> >+template class std::map<T, int, L,
> >+                      CustomPointerAlloc<std::pair<const T, int>>>;
> >+
> >+void test01()
> >+{
> >+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
> >+  typedef std::map<T, int, L, alloc_type> test_type;
> >+  test_type v;
> >+  v.insert({ T(), 0 });
> >+  VERIFY( v.begin()->second == 0 );
> >+  VERIFY( ++v.begin() == v.end() );
> >+  v.insert({ T { 1 }, 1 });
> >+  VERIFY( v.size() == 2 );
> >+  VERIFY( v.find(T()) != v.end() );
> >+}
> >+
> >+int main()
> >+{
> >+  test01();
> >+}
> >diff --git a/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
> >new file mode 100644
> >index 00000000000..41689ef43b7
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
> >@@ -0,0 +1,33 @@
> >+// { dg-do run { target c++11 } }
> >+
> >+#include <map>
> >+#include <memory>
> >+#include <testsuite_hooks.h>
> >+#include <testsuite_allocator.h>
> >+
> >+struct T { int i; };
> >+
> >+bool operator<(const T& l, const T& r)
> >+{ return l.i < r.i; }
> >+
> >+struct L : std::less<T>
> >+{ };
> >+
> >+using __gnu_test::CustomPointerAlloc;
> >+
> >+template class std::multimap<T, int, L,
> >+                      CustomPointerAlloc<std::pair<const T, int>>>;
> >+
> >+void test01()
> >+{
> >+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
> >+  typedef std::multimap<T, int, L, alloc_type> test_type;
> >+  test_type v;
> >+  v.insert({ T(), 0 });
> >+  VERIFY( ++v.begin() == v.end() );
> >+}
> >+
> >+int main()
> >+{
> >+  test01();
> >+}
> >diff --git a/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
> >new file mode 100644
> >index 00000000000..589e6b0a617
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
> >@@ -0,0 +1,32 @@
> >+// { dg-do run { target c++11 } }
> >+
> >+#include <set>
> >+#include <memory>
> >+#include <testsuite_hooks.h>
> >+#include <testsuite_allocator.h>
> >+
> >+struct T { int i; };
> >+
> >+bool operator<(const T& l, const T& r)
> >+{ return l.i < r.i; }
> >+
> >+struct L : std::less<T>
> >+{ };
> >+
> >+using __gnu_test::CustomPointerAlloc;
> >+
> >+template class std::multiset<T, L, CustomPointerAlloc<T>>;
> >+
> >+void test01()
> >+{
> >+  typedef CustomPointerAlloc<T> alloc_type;
> >+  typedef std::multiset<T, L, alloc_type> test_type;
> >+  test_type v;
> >+  v.insert(T());
> >+  VERIFY( ++v.begin() == v.end() );
> >+}
> >+
> >+int main()
> >+{
> >+  test01();
> >+}
> >diff --git a/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
> >new file mode 100644
> >index 00000000000..bbcf32b20be
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
> >@@ -0,0 +1,32 @@
> >+// { dg-do run { target c++11 } }
> >+
> >+#include <set>
> >+#include <memory>
> >+#include <testsuite_hooks.h>
> >+#include <testsuite_allocator.h>
> >+
> >+struct T { int i; };
> >+
> >+bool operator<(const T& l, const T& r)
> >+{ return l.i < r.i; }
> >+
> >+struct L : std::less<T>
> >+{ };
> >+
> >+using __gnu_test::CustomPointerAlloc;
> >+
> >+template class std::set<T, L, CustomPointerAlloc<T>>;
> >+
> >+void test01()
> >+{
> >+  typedef CustomPointerAlloc<T> alloc_type;
> >+  typedef std::set<T, L, alloc_type> test_type;
> >+  test_type v;
> >+  v.insert(T());
> >+  VERIFY( ++v.begin() == v.end() );
> >+}
> >+
> >+int main()
> >+{
> >+  test01();
> >+}
>
  
François Dumont Dec. 9, 2024, 6:05 a.m. UTC | #19
On 04/12/2024 22:48, Jonathan Wakely wrote:
> On 04/12/24 19:27 +0100, François Dumont wrote:
>> Hi
>>
>> I've completed the synchronization with your equivalent PR for 
>> std::list so here is the updated patch.
>>
>> PR updated a couple of days ago.
>>
>> Note that I've started to rework the patch for the same in _Hashtable.
>
> Great, thanks.
>
>>
>> #if __cplusplus < 201103L
>>       void
>> -      _M_construct_node(_Link_type __node, const value_type& __x)
>> +      _M_construct_node(_Base_ptr __p, const value_type& __x)
>
> And can this be left as _Node_ptr instead of changed to _Base_ptr?
> Then it wouldn't need the static_cast, and the callers wouldn't need
> to use _M_base_ptr().
>
> Logically, it has to be a _Node_ptr or the cast isn't valid anyway.
>
> There seem to be several places like this where we're giving up
> valuable type information by using _base_ptr instead of _Node_ptr.
>
> Is that really necessary? If we have a _Node_ptr we can always get a
> _Base_ptr easily by calling _M_base_ptr().
>
> Is it a constness thing, because _M_begin() const was changed to
> return _Base_ptr instead of _Const_link_ptr?
>
No, just me being lazy and considering that as we store _Base_ptr we can 
also use it everywhere.

In this new version I've preserved _Node_ptr almost everywhere.

But _M_begin() is still returning a _Base_ptr and I've introduced 
_M_begin_node() for rare occasions when a _Node_ptr is needed.

Note that I've hesitated to add a _S_nullptr() to get a _Base_ptr null 
pointer. Is default constructor on fancy pointer type mandatory ? I fear 
that only constructor from nullptr is mandatory.

I've forced push on:

https://forge.sourceware.org/gcc/gcc-TEST/pulls/27

François
diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
index bc27e191e8b..c5539371628 100644
--- a/libstdc++-v3/include/bits/stl_tree.h
+++ b/libstdc++-v3/include/bits/stl_tree.h
@@ -66,6 +66,7 @@
 #include <bits/allocator.h>
 #include <bits/stl_function.h>
 #include <bits/cpp_type_traits.h>
+#include <bits/ptr_traits.h>
 #include <ext/alloc_traits.h>
 #if __cplusplus >= 201103L
 # include <ext/aligned_buffer.h>
@@ -74,6 +75,13 @@
 # include <bits/node_handle.h>
 #endif
 
+#if __cplusplus < 201103L
+# undef _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+# define _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE 0
+#elif ! defined _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+# define _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE 1
+#endif
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -99,7 +107,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   struct _Rb_tree_node_base
   {
     typedef _Rb_tree_node_base* _Base_ptr;
-    typedef const _Rb_tree_node_base* _Const_Base_ptr;
 
     _Rb_tree_color	_M_color;
     _Base_ptr		_M_parent;
@@ -113,13 +120,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return __x;
     }
 
-    static _Const_Base_ptr
-    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-    {
-      while (__x->_M_left != 0) __x = __x->_M_left;
-      return __x;
-    }
-
     static _Base_ptr
     _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
     {
@@ -127,12 +127,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return __x;
     }
 
-    static _Const_Base_ptr
-    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-    {
-      while (__x->_M_right != 0) __x = __x->_M_right;
-      return __x;
-    }
+    // This is not const-correct, but it's only used in a const access path
+    // by std::_Rb_tree::_M_end() where the pointer is used to initialize a
+    // const_iterator and so constness is restored.
+    _Base_ptr
+    _M_base_ptr() const _GLIBCXX_NOEXCEPT
+    { return const_cast<_Rb_tree_node_base*>(this); }
+
+    template<typename _NodePtr>
+      _NodePtr
+      _M_node_ptr() _GLIBCXX_NOEXCEPT
+      { return static_cast<_NodePtr>(this); }
   };
 
   // Helper type offering value initialization guarantee on the compare functor.
@@ -213,8 +218,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Val>
     struct _Rb_tree_node : public _Rb_tree_node_base
     {
-      typedef _Rb_tree_node<_Val>* _Link_type;
-
 #if __cplusplus < 201103L
       _Val _M_value_field;
 
@@ -238,18 +241,135 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
     };
 
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+namespace __rb_tree
+{
+  template<typename _VoidPtr>
+    struct _Node_base
+    {
+      using _Base_ptr = __ptr_rebind<_VoidPtr, _Node_base>;
+
+      _Rb_tree_color	_M_color;
+      _Base_ptr		_M_parent;
+      _Base_ptr		_M_left;
+      _Base_ptr		_M_right;
+
+      static _Base_ptr
+      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      {
+	while (__x->_M_left) __x = __x->_M_left;
+	return __x;
+      }
+
+      static _Base_ptr
+      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      {
+	while (__x->_M_right) __x = __x->_M_right;
+	return __x;
+      }
+
+      // This is not const-correct, but it's only used in a const access path
+      // by std::_Rb_tree::_M_end() where the pointer is used to initialize a
+      // const_iterator and so constness is restored.
+      _Base_ptr
+      _M_base_ptr() const
+      {
+	return pointer_traits<_Base_ptr>::pointer_to
+	  (*const_cast<_Node_base*>(this));
+      }
+
+      template<typename _NodePtr>
+	_NodePtr
+	_M_node_ptr() _GLIBCXX_NOEXCEPT
+	{
+	  using __ptr_traits = pointer_traits<_NodePtr>;
+	  using __node_type = typename __ptr_traits::element_type;
+	  auto __this = static_cast<__node_type*>(this);
+	  return __ptr_traits::pointer_to(*__this);
+	}
+    };
+
+  // Helper type to manage default initialization of node count and header.
+  template<typename _NodeBase>
+    struct _Header
+    {
+    private:
+      using _Base_ptr =  typename _NodeBase::_Base_ptr;
+
+    public:
+      _NodeBase		_M_header;
+      size_t		_M_node_count; // Keeps track of size of tree.
+
+      _Header() noexcept
+      {
+	_M_header._M_color = _S_red;
+	_M_reset();
+      }
+
+      _Header(_Header&& __x) noexcept
+      {
+	if (__x._M_header._M_parent)
+	  _M_move_data(__x);
+	else
+	  {
+	    _M_header._M_color = _S_red;
+	    _M_reset();
+	  }
+      }
+
+      void
+      _M_move_data(_Header& __from)
+      {
+	_M_header._M_color = __from._M_header._M_color;
+	_M_header._M_parent = __from._M_header._M_parent;
+	_M_header._M_left = __from._M_header._M_left;
+	_M_header._M_right = __from._M_header._M_right;
+	_M_header._M_parent->_M_parent = _M_header._M_base_ptr();
+	_M_node_count = __from._M_node_count;
+
+	__from._M_reset();
+      }
+
+      void
+      _M_reset()
+      {
+	_M_header._M_parent = _Base_ptr{};
+	_M_header._M_left = _M_header._M_right = _M_header._M_base_ptr();
+	_M_node_count = 0;
+      }
+    };
+
+  template<typename _ValPtr>
+    struct _Node : public __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>
+    {
+      using value_type = typename pointer_traits<_ValPtr>::element_type;
+
+      union
+      {
+	value_type _M_storage;
+      };
+
+      _Node() { }
+      ~_Node() { }
+      _Node(_Node&&) = delete;
+
+      value_type*
+      _M_valptr()
+      { return std::__addressof(_M_storage); }
+
+      value_type const*
+      _M_valptr() const
+      { return std::__addressof(_M_storage); }
+    };
+} // namespace __rb_tree
+#endif
+
   _GLIBCXX_PURE _Rb_tree_node_base*
   _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
 
-  _GLIBCXX_PURE const _Rb_tree_node_base*
-  _Rb_tree_increment(const _Rb_tree_node_base* __x) throw ();
-
   _GLIBCXX_PURE _Rb_tree_node_base*
   _Rb_tree_decrement(_Rb_tree_node_base* __x) throw ();
 
-  _GLIBCXX_PURE const _Rb_tree_node_base*
-  _Rb_tree_decrement(const _Rb_tree_node_base* __x) throw ();
-
   template<typename _Tp>
     struct _Rb_tree_iterator
     {
@@ -260,9 +380,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef bidirectional_iterator_tag iterator_category;
       typedef ptrdiff_t			 difference_type;
 
-      typedef _Rb_tree_iterator<_Tp>		_Self;
       typedef _Rb_tree_node_base::_Base_ptr	_Base_ptr;
-      typedef _Rb_tree_node<_Tp>*		_Link_type;
+      typedef _Rb_tree_node<_Tp>*		_Node_ptr;
 
       _Rb_tree_iterator() _GLIBCXX_NOEXCEPT
       : _M_node() { }
@@ -273,49 +392,51 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       reference
       operator*() const _GLIBCXX_NOEXCEPT
-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type> (_M_node)->_M_valptr(); }
+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
-      _Self&
+      _Rb_tree_iterator&
       operator++() _GLIBCXX_NOEXCEPT
       {
 	_M_node = _Rb_tree_increment(_M_node);
 	return *this;
       }
 
-      _Self
+      _Rb_tree_iterator
       operator++(int) _GLIBCXX_NOEXCEPT
       {
-	_Self __tmp = *this;
+	_Rb_tree_iterator __tmp = *this;
 	_M_node = _Rb_tree_increment(_M_node);
 	return __tmp;
       }
 
-      _Self&
+      _Rb_tree_iterator&
       operator--() _GLIBCXX_NOEXCEPT
       {
 	_M_node = _Rb_tree_decrement(_M_node);
 	return *this;
       }
 
-      _Self
+      _Rb_tree_iterator
       operator--(int) _GLIBCXX_NOEXCEPT
       {
-	_Self __tmp = *this;
+	_Rb_tree_iterator __tmp = *this;
 	_M_node = _Rb_tree_decrement(_M_node);
 	return __tmp;
       }
 
       friend bool
-      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      operator==(const _Rb_tree_iterator& __x,
+		 const _Rb_tree_iterator& __y) _GLIBCXX_NOEXCEPT
       { return __x._M_node == __y._M_node; }
 
 #if ! __cpp_lib_three_way_comparison
       friend bool
-      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      operator!=(const _Rb_tree_iterator& __x,
+		 const _Rb_tree_iterator& __y) _GLIBCXX_NOEXCEPT
       { return __x._M_node != __y._M_node; }
 #endif
 
@@ -334,9 +455,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef bidirectional_iterator_tag iterator_category;
       typedef ptrdiff_t			 difference_type;
 
-      typedef _Rb_tree_const_iterator<_Tp>		_Self;
-      typedef _Rb_tree_node_base::_Const_Base_ptr	_Base_ptr;
-      typedef const _Rb_tree_node<_Tp>*			_Link_type;
+      typedef _Rb_tree_node_base::_Base_ptr	_Base_ptr;
+      typedef const _Rb_tree_node<_Tp>*		_Node_ptr;
 
       _Rb_tree_const_iterator() _GLIBCXX_NOEXCEPT
       : _M_node() { }
@@ -348,55 +468,53 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree_const_iterator(const iterator& __it) _GLIBCXX_NOEXCEPT
       : _M_node(__it._M_node) { }
 
-      iterator
-      _M_const_cast() const _GLIBCXX_NOEXCEPT
-      { return iterator(const_cast<typename iterator::_Base_ptr>(_M_node)); }
-
       reference
       operator*() const _GLIBCXX_NOEXCEPT
-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
-      _Self&
+      _Rb_tree_const_iterator&
       operator++() _GLIBCXX_NOEXCEPT
       {
 	_M_node = _Rb_tree_increment(_M_node);
 	return *this;
       }
 
-      _Self
+      _Rb_tree_const_iterator
       operator++(int) _GLIBCXX_NOEXCEPT
       {
-	_Self __tmp = *this;
+	_Rb_tree_const_iterator __tmp = *this;
 	_M_node = _Rb_tree_increment(_M_node);
 	return __tmp;
       }
 
-      _Self&
+      _Rb_tree_const_iterator&
       operator--() _GLIBCXX_NOEXCEPT
       {
 	_M_node = _Rb_tree_decrement(_M_node);
 	return *this;
       }
 
-      _Self
+      _Rb_tree_const_iterator
       operator--(int) _GLIBCXX_NOEXCEPT
       {
-	_Self __tmp = *this;
+	_Rb_tree_const_iterator __tmp = *this;
 	_M_node = _Rb_tree_decrement(_M_node);
 	return __tmp;
       }
 
       friend bool
-      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      operator==(const _Rb_tree_const_iterator& __x,
+		 const _Rb_tree_const_iterator& __y) _GLIBCXX_NOEXCEPT
       { return __x._M_node == __y._M_node; }
 
 #if ! __cpp_lib_three_way_comparison
       friend bool
-      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      operator!=(const _Rb_tree_const_iterator& __x,
+		 const _Rb_tree_const_iterator& __y) _GLIBCXX_NOEXCEPT
       { return __x._M_node != __y._M_node; }
 #endif
 
@@ -415,6 +533,485 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
 			       _Rb_tree_node_base& __header) throw ();
 
+namespace __rb_tree
+{
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+  template<bool _Const, typename _ValPtr>
+    struct _Iterator
+    {
+      template<typename _Tp>
+	using __maybe_const = __conditional_t<_Const, const _Tp, _Tp>;
+
+      using __ptr_traits =	pointer_traits<_ValPtr>;
+      using value_type =	typename __ptr_traits::element_type;
+      using reference =		__maybe_const<value_type>&;
+      using pointer =		__maybe_const<value_type>*;
+
+      using iterator_category =	bidirectional_iterator_tag;
+      using difference_type =	ptrdiff_t;
+
+      using _Node = __rb_tree::_Node<_ValPtr>;
+      using _Node_base = __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>;
+      using _Base_ptr =	 typename _Node_base::_Base_ptr;
+
+      _Iterator() _GLIBCXX_NOEXCEPT
+      : _M_node() { }
+
+      constexpr explicit
+      _Iterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      : _M_node(__x) { }
+
+#if __cplusplus >= 201103L
+      _Iterator(const _Iterator&) = default;
+      _Iterator& operator=(const _Iterator&) = default;
+#endif
+
+#ifdef __glibcxx_concepts
+      constexpr
+      _Iterator(const _Iterator<false, _ValPtr>& __it) requires _Const
+#else
+      template<bool _OtherConst,
+	       typename = __enable_if_t<_Const && !_OtherConst>>
+	constexpr
+	_Iterator(const _Iterator<_OtherConst, _ValPtr>& __it)
+#endif
+	: _M_node(__it._M_node) { }
+
+      [[__nodiscard__]]
+      reference
+      operator*() const _GLIBCXX_NOEXCEPT
+      { return *static_cast<_Node&>(*_M_node)._M_valptr(); }
+
+      [[__nodiscard__]]
+      pointer
+      operator->() const _GLIBCXX_NOEXCEPT
+      { return static_cast<_Node&>(*_M_node)._M_valptr(); }
+
+      _GLIBCXX14_CONSTEXPR _Iterator&
+      operator++() _GLIBCXX_NOEXCEPT
+      {
+	if (_M_node->_M_right)
+	  {
+	    _M_node = _M_node->_M_right;
+	    while (_M_node->_M_left)
+	      _M_node = _M_node->_M_left;
+	  }
+	else
+	  {
+	    _Base_ptr __y = _M_node->_M_parent;
+	    while (_M_node == __y->_M_right)
+	      {
+		_M_node = __y;
+		__y = __y->_M_parent;
+	      }
+	    if (_M_node->_M_right != __y)
+	      _M_node = __y;
+	  }
+
+	return *this;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator
+      operator++(int) _GLIBCXX_NOEXCEPT
+      {
+	_Iterator __tmp(this->_M_node);
+	++*this;
+	return __tmp;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator&
+      operator--() _GLIBCXX_NOEXCEPT
+      {
+	if (_M_node->_M_color == _S_red
+	    && _M_node->_M_parent->_M_parent == _M_node)
+	  _M_node = _M_node->_M_right;
+	else if (_M_node->_M_left)
+	  {
+	    _Base_ptr __y = _M_node->_M_left;
+	    while (__y->_M_right)
+	      __y = __y->_M_right;
+	    _M_node = __y;
+	  }
+	else
+	  {
+	    _Base_ptr __y = _M_node->_M_parent;
+	    while (_M_node == __y->_M_left)
+	      {
+		_M_node = __y;
+		__y = __y->_M_parent;
+	      }
+	    _M_node = __y;
+	  }
+	return *this;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator
+      operator--(int) _GLIBCXX_NOEXCEPT
+      {
+	_Iterator __tmp(this->_M_node);
+	--*this;
+	return __tmp;
+      }
+
+      [[__nodiscard__]]
+      friend bool
+      operator==(const _Iterator& __x, const _Iterator& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node == __y._M_node; }
+
+#if ! __cpp_lib_three_way_comparison
+      [[__nodiscard__]]
+      friend bool
+      operator!=(const _Iterator& __x, const _Iterator& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node != __y._M_node; }
+#endif
+
+      _Base_ptr _M_node;
+    };
+#endif // USE_ALLOC_PTR_FOR_RB_TREE
+
+  // Determine the node and iterator types used by std::_Rb_tree.
+  template<typename _Val, typename _Ptr>
+    struct _Node_traits;
+
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE <= 9000
+  // Specialization for the simple case where the allocator's pointer type
+  // is the same type as value_type*.
+  // For ABI compatibility we can't change the types used for this case.
+  template<typename _Val>
+    struct _Node_traits<_Val, _Val*>
+    {
+      typedef _Rb_tree_node<_Val>		_Node;
+      typedef _Rb_tree_node_base		_Node_base;
+      typedef _Rb_tree_header			_Header_t;
+      typedef _Rb_tree_iterator<_Val>		_Iterator;
+      typedef _Rb_tree_const_iterator<_Val>	_Const_iterator;
+
+      __attribute__((__nonnull__))
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _Node_base* __x, _Node_base* __p,
+			      _Node_base& __header) _GLIBCXX_USE_NOEXCEPT
+      {
+	return _Rb_tree_insert_and_rebalance(__insert_left, __x, __p, __header);
+      }
+
+      __attribute__((__nonnull__,__returns_nonnull__))
+      static _Node_base*
+      _S_rebalance_for_erase(_Node_base* const __z,
+			     _Node_base& __header) _GLIBCXX_USE_NOEXCEPT
+      { return _Rb_tree_rebalance_for_erase(__z, __header); }
+    };
+#endif
+
+#if ! _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+  // Always use the T* specialization.
+  template<typename _Val, typename _Ptr>
+    struct _Node_traits
+    : _Node_traits<_Val, _Val*>
+    { };
+#else
+  // Primary template used when the allocator uses fancy pointers.
+  template<typename _Val, typename _ValPtr>
+    struct _Node_traits
+    {
+      using _Node = __rb_tree::_Node<_ValPtr>;
+      using _Node_base = __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>;
+      using _Header_t = __rb_tree::_Header<_Node_base>;
+      using _Iterator = __rb_tree::_Iterator<false, _ValPtr>;
+      using _Const_iterator = __rb_tree::_Iterator<true, _ValPtr>;
+
+      using _Base_ptr = typename _Node_base::_Base_ptr;
+
+      static void
+      _Rotate_left(_Base_ptr __x, _Base_ptr& __root)
+      {
+	const _Base_ptr __y = __x->_M_right;
+
+	__x->_M_right = __y->_M_left;
+	if (__y->_M_left)
+	  __y->_M_left->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_left)
+	  __x->_M_parent->_M_left = __y;
+	else
+	  __x->_M_parent->_M_right = __y;
+	__y->_M_left = __x;
+	__x->_M_parent = __y;
+      }
+
+      static void
+      _Rotate_right(_Base_ptr __x, _Base_ptr& __root)
+      {
+	const _Base_ptr __y = __x->_M_left;
+
+	__x->_M_left = __y->_M_right;
+	if (__y->_M_right)
+	  __y->_M_right->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_right)
+	  __x->_M_parent->_M_right = __y;
+	else
+	  __x->_M_parent->_M_left = __y;
+	__y->_M_right = __x;
+	__x->_M_parent = __y;
+      }
+
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _Base_ptr __x, _Base_ptr __p,
+			      _Node_base& __header)
+      {
+	_Base_ptr& __root = __header._M_parent;
+
+	// Initialize fields in new node to insert.
+	__x->_M_parent = __p;
+	__x->_M_left = __x->_M_right = nullptr;
+	__x->_M_color = _S_red;
+
+	// Insert.
+	// Make new node child of parent and maintain root, leftmost and
+	// rightmost nodes.
+	// N.B. First node is always inserted left.
+	if (__insert_left)
+	  {
+	    __p->_M_left = __x; // also makes leftmost = __x when __p == &__header
+
+	    if (std::__to_address(__p) == std::addressof(__header))
+	      {
+		__header._M_parent = __x;
+		__header._M_right = __x;
+	      }
+	    else if (__p == __header._M_left)
+	      __header._M_left = __x; // maintain leftmost pointing to min node
+	  }
+	else
+	  {
+	    __p->_M_right = __x;
+
+	    if (__p == __header._M_right)
+	      __header._M_right = __x; // maintain rightmost pointing to max node
+	  }
+	// Rebalance.
+	while (__x != __root
+	       && __x->_M_parent->_M_color == _S_red)
+	  {
+	    const _Base_ptr __xpp = __x->_M_parent->_M_parent;
+
+	    if (__x->_M_parent == __xpp->_M_left)
+	      {
+		const _Base_ptr __y = __xpp->_M_right;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_right)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_left(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_right(__xpp, __root);
+		  }
+	      }
+	    else
+	      {
+		const _Base_ptr __y = __xpp->_M_left;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_left)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_right(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_left(__xpp, __root);
+		  }
+	      }
+	  }
+	__root->_M_color = _S_black;
+      }
+
+      static _Base_ptr
+      _S_rebalance_for_erase(_Base_ptr __z, _Node_base& __header)
+      {
+	_Base_ptr& __root = __header._M_parent;
+	_Base_ptr& __leftmost = __header._M_left;
+	_Base_ptr& __rightmost = __header._M_right;
+	_Base_ptr __y = __z;
+	_Base_ptr __x{};
+	_Base_ptr __x_parent{};
+
+	if (!__y->_M_left)     // __z has at most one non-null child. y == z.
+	  __x = __y->_M_right;     // __x might be null.
+	else
+	  if (!__y->_M_right)  // __z has exactly one non-null child. y == z.
+	    __x = __y->_M_left;    // __x is not null.
+	  else
+	    {
+	      // __z has two non-null children.  Set __y to
+	      __y = __y->_M_right;   //   __z's successor.  __x might be null.
+	      while (__y->_M_left)
+		__y = __y->_M_left;
+	      __x = __y->_M_right;
+	    }
+	if (__y != __z)
+	  {
+	    // relink y in place of z.  y is z's successor
+	    __z->_M_left->_M_parent = __y;
+	    __y->_M_left = __z->_M_left;
+	    if (__y != __z->_M_right)
+	      {
+		__x_parent = __y->_M_parent;
+		if (__x)
+		  __x->_M_parent = __y->_M_parent;
+		__y->_M_parent->_M_left = __x;   // __y must be a child of _M_left
+		__y->_M_right = __z->_M_right;
+		__z->_M_right->_M_parent = __y;
+	      }
+	    else
+	      __x_parent = __y;
+	    if (__root == __z)
+	      __root = __y;
+	    else if (__z->_M_parent->_M_left == __z)
+	      __z->_M_parent->_M_left = __y;
+	    else
+	      __z->_M_parent->_M_right = __y;
+	    __y->_M_parent = __z->_M_parent;
+	    std::swap(__y->_M_color, __z->_M_color);
+	    __y = __z;
+	    // __y now points to node to be actually deleted
+	  }
+	else
+	  {                        // __y == __z
+	    __x_parent = __y->_M_parent;
+	    if (__x)
+	      __x->_M_parent = __y->_M_parent;
+	    if (__root == __z)
+	      __root = __x;
+	    else
+	      if (__z->_M_parent->_M_left == __z)
+		__z->_M_parent->_M_left = __x;
+	      else
+		__z->_M_parent->_M_right = __x;
+	    if (__leftmost == __z)
+	      {
+		if (!__z->_M_right)        // __z->_M_left must be null also
+		  __leftmost = __z->_M_parent;
+		// makes __leftmost == _M_header if __z == __root
+		else
+		  __leftmost = _Node_base::_S_minimum(__x);
+	      }
+	    if (__rightmost == __z)
+	      {
+		if (__z->_M_left == 0)         // __z->_M_right must be null also
+		  __rightmost = __z->_M_parent;
+		// makes __rightmost == _M_header if __z == __root
+		else                      // __x == __z->_M_left
+		  __rightmost = _Node_base::_S_maximum(__x);
+	      }
+	  }
+	if (__y->_M_color != _S_red)
+	  {
+	    while (__x != __root && (__x == 0 || __x->_M_color == _S_black))
+	      if (__x == __x_parent->_M_left)
+		{
+		  _Base_ptr __w = __x_parent->_M_right;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_left(__x_parent, __root);
+		      __w = __x_parent->_M_right;
+		    }
+		  if ((!__w->_M_left || __w->_M_left->_M_color == _S_black) &&
+		      (!__w->_M_right || __w->_M_right->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_right || __w->_M_right->_M_color == _S_black)
+			{
+			  __w->_M_left->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_right(__w, __root);
+			  __w = __x_parent->_M_right;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_right)
+			__w->_M_right->_M_color = _S_black;
+		      _Rotate_left(__x_parent, __root);
+		      break;
+		    }
+		}
+	      else
+		{
+		  // same as above, with _M_right <-> _M_left.
+		  _Base_ptr __w = __x_parent->_M_left;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_right(__x_parent, __root);
+		      __w = __x_parent->_M_left;
+		    }
+		  if ((!__w->_M_right || __w->_M_right->_M_color == _S_black) &&
+		      (!__w->_M_left || __w->_M_left->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_left || __w->_M_left->_M_color == _S_black)
+			{
+			  __w->_M_right->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_left(__w, __root);
+			  __w = __x_parent->_M_left;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_left)
+			__w->_M_left->_M_color = _S_black;
+		      _Rotate_right(__x_parent, __root);
+		      break;
+		    }
+		}
+	    if (__x)
+	      __x->_M_color = _S_black;
+	  }
+
+	return __y;
+      }
+    };
+#endif
+} // namespace __rb_tree
+
 #if __cplusplus > 201402L
   template<typename _Tree1, typename _Cmp2>
     struct _Rb_tree_merge_helper { };
@@ -425,15 +1022,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     class _Rb_tree
     {
       typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
-	rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
+	rebind<_Val>::other _Val_alloc_type;
+
+      typedef __gnu_cxx::__alloc_traits<_Val_alloc_type> _Val_alloc_traits;
+      typedef typename _Val_alloc_traits::pointer _ValPtr;
+      typedef __rb_tree::_Node_traits<_Val, _ValPtr> _Node_traits;
+
+      typedef typename _Node_traits::_Node_base		_Node_base;
+      typedef typename _Node_traits::_Node		_Node;
+
+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
+	rebind<_Node>::other _Node_allocator;
 
-      typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
+      typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Node_alloc_traits;
 
     protected:
-      typedef _Rb_tree_node_base* 		_Base_ptr;
-      typedef const _Rb_tree_node_base* 	_Const_Base_ptr;
-      typedef _Rb_tree_node<_Val>* 		_Link_type;
-      typedef const _Rb_tree_node<_Val>*	_Const_Link_type;
+      typedef typename _Node_base::_Base_ptr 		_Base_ptr;
+      typedef typename _Node_alloc_traits::pointer	_Node_ptr;
 
     private:
       // Functor recycling a pool of nodes and using allocation once the pool
@@ -445,13 +1050,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  if (_M_root)
 	    {
-	      _M_root->_M_parent = 0;
+	      _M_root->_M_parent = _Base_ptr();
 
 	      if (_M_nodes->_M_left)
 		_M_nodes = _M_nodes->_M_left;
 	    }
 	  else
-	    _M_nodes = 0;
+	    _M_nodes = _Base_ptr();
 	}
 
 #if __cplusplus >= 201103L
@@ -459,15 +1064,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
 	~_Reuse_or_alloc_node()
-	{ _M_t._M_erase(static_cast<_Link_type>(_M_root)); }
+	{
+	  if (_M_root)
+	    _M_t._M_erase(_M_root->template _M_node_ptr<_Node_ptr>());
+	}
 
 	template<typename _Arg>
-	  _Link_type
+	  _Node_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg)
 	  {
-	    _Link_type __node = static_cast<_Link_type>(_M_extract());
-	    if (__node)
+	    _Base_ptr __base = _M_extract();
+	    if (__base)
 	      {
+		_Node_ptr __node = __base->template _M_node_ptr<_Node_ptr>();
 		_M_t._M_destroy_node(__node);
 		_M_t._M_construct_node(__node, _GLIBCXX_FORWARD(_Arg, __arg));
 		return __node;
@@ -489,7 +1098,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    {
 	      if (_M_nodes->_M_right == __node)
 		{
-		  _M_nodes->_M_right = 0;
+		  _M_nodes->_M_right = _Base_ptr();
 
 		  if (_M_nodes->_M_left)
 		    {
@@ -503,10 +1112,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		    }
 		}
 	      else // __node is on the left.
-		_M_nodes->_M_left = 0;
+		_M_nodes->_M_left = _Base_ptr();
 	    }
 	  else
-	    _M_root = 0;
+	    _M_root = _Base_ptr();
 
 	  return __node;
 	}
@@ -524,7 +1133,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: _M_t(__t) { }
 
 	template<typename _Arg>
-	  _Link_type
+	  _Node_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg) const
 	  { return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg)); }
 
@@ -556,17 +1165,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return allocator_type(_M_get_Node_allocator()); }
 
     protected:
-      _Link_type
+      _Node_ptr
       _M_get_node()
-      { return _Alloc_traits::allocate(_M_get_Node_allocator(), 1); }
+      { return _Node_alloc_traits::allocate(_M_get_Node_allocator(), 1); }
 
       void
-      _M_put_node(_Link_type __p) _GLIBCXX_NOEXCEPT
-      { _Alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1); }
+      _M_put_node(_Node_ptr __p) _GLIBCXX_NOEXCEPT
+      { _Node_alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1); }
 
 #if __cplusplus < 201103L
       void
-      _M_construct_node(_Link_type __node, const value_type& __x)
+      _M_construct_node(_Node_ptr __node, const value_type& __x)
       {
 	__try
 	  { get_allocator().construct(__node->_M_valptr(), __x); }
@@ -577,79 +1186,80 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  }
       }
 
-      _Link_type
+      _Node_ptr
       _M_create_node(const value_type& __x)
       {
-	_Link_type __tmp = _M_get_node();
+	_Node_ptr __tmp = _M_get_node();
 	_M_construct_node(__tmp, __x);
 	return __tmp;
       }
 #else
       template<typename... _Args>
 	void
-	_M_construct_node(_Link_type __node, _Args&&... __args)
+	_M_construct_node(_Node_ptr __node, _Args&&... __args)
 	{
 	  __try
 	    {
-	      ::new(__node) _Rb_tree_node<_Val>;
-	      _Alloc_traits::construct(_M_get_Node_allocator(),
-				       __node->_M_valptr(),
-				       std::forward<_Args>(__args)...);
+	      ::new(std::__addressof(*__node)) _Node;
+	      _Node_alloc_traits::construct(_M_get_Node_allocator(),
+					    __node->_M_valptr(),
+					    std::forward<_Args>(__args)...);
 	    }
 	  __catch(...)
 	    {
-	      __node->~_Rb_tree_node<_Val>();
+	      __node->~_Node();
 	      _M_put_node(__node);
 	      __throw_exception_again;
 	    }
 	}
 
       template<typename... _Args>
-	_Link_type
+	_Node_ptr
 	_M_create_node(_Args&&... __args)
 	{
-	  _Link_type __tmp = _M_get_node();
+	  _Node_ptr __tmp = _M_get_node();
 	  _M_construct_node(__tmp, std::forward<_Args>(__args)...);
 	  return __tmp;
 	}
 #endif
 
       void
-      _M_destroy_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_destroy_node(_Node_ptr __p) _GLIBCXX_NOEXCEPT
       {
 #if __cplusplus < 201103L
 	get_allocator().destroy(__p->_M_valptr());
 #else
-	_Alloc_traits::destroy(_M_get_Node_allocator(), __p->_M_valptr());
-	__p->~_Rb_tree_node<_Val>();
+	_Node_alloc_traits::destroy(_M_get_Node_allocator(), __p->_M_valptr());
+	__p->~_Node();
 #endif
       }
 
       void
-      _M_drop_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_drop_node(_Node_ptr __p) _GLIBCXX_NOEXCEPT
       {
 	_M_destroy_node(__p);
 	_M_put_node(__p);
       }
 
       template<bool _MoveValue, typename _NodeGen>
-	_Link_type
-	_M_clone_node(_Link_type __x, _NodeGen& __node_gen)
+	_Node_ptr
+	_M_clone_node(_Node_ptr __x, _NodeGen& __node_gen)
 	{
 #if __cplusplus >= 201103L
 	  using _Vp = __conditional_t<_MoveValue,
 				      value_type&&,
 				      const value_type&>;
 #endif
-	  _Link_type __tmp
+	  _Node_ptr __tmp
 	    = __node_gen(_GLIBCXX_FORWARD(_Vp, *__x->_M_valptr()));
 	  __tmp->_M_color = __x->_M_color;
-	  __tmp->_M_left = 0;
-	  __tmp->_M_right = 0;
+	  __tmp->_M_left = __tmp->_M_right = _Base_ptr();
 	  return __tmp;
 	}
 
     protected:
+      typedef typename _Node_traits::_Header_t		_Header_t;
+
 #if _GLIBCXX_INLINE_VERSION
       template<typename _Key_compare>
 #else
@@ -660,7 +1270,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	struct _Rb_tree_impl
 	: public _Node_allocator
 	, public _Rb_tree_key_compare<_Key_compare>
-	, public _Rb_tree_header
+	, public _Header_t
 	{
 	  typedef _Rb_tree_key_compare<_Key_compare> _Base_key_compare;
 
@@ -672,9 +1282,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  { }
 
 	  _Rb_tree_impl(const _Rb_tree_impl& __x)
-	  : _Node_allocator(_Alloc_traits::_S_select_on_copy(__x))
+	  : _Node_allocator(_Node_alloc_traits::_S_select_on_copy(__x))
 	  , _Base_key_compare(__x._M_key_compare)
-	  , _Rb_tree_header()
+	  , _Header_t()
 	  { }
 
 #if __cplusplus < 201103L
@@ -694,7 +1304,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Rb_tree_impl(_Rb_tree_impl&& __x, _Node_allocator&& __a)
 	  : _Node_allocator(std::move(__a)),
 	    _Base_key_compare(std::move(__x)),
-	    _Rb_tree_header(std::move(__x))
+	    _Header_t(std::move(__x))
 	  { }
 
 	  _Rb_tree_impl(const _Key_compare& __comp, _Node_allocator&& __a)
@@ -710,7 +1320,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_root() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_parent; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_root() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_parent; }
 
@@ -718,7 +1328,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_leftmost() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_left; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_leftmost() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_left; }
 
@@ -726,35 +1336,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_rightmost() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_right; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_rightmost() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_right; }
 
-      _Link_type
-      _M_mbegin() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(this->_M_impl._M_header._M_parent); }
-
-      _Link_type
-      _M_begin() _GLIBCXX_NOEXCEPT
-      { return _M_mbegin(); }
-
-      _Const_Link_type
+      _Base_ptr
       _M_begin() const _GLIBCXX_NOEXCEPT
+      { return this->_M_impl._M_header._M_parent; }
+
+      _Node_ptr
+      _M_begin_node() const _GLIBCXX_NOEXCEPT
       {
-	return static_cast<_Const_Link_type>
-	  (this->_M_impl._M_header._M_parent);
+	_Base_ptr __begin = this->_M_impl._M_header._M_parent;
+	return __begin
+	  ? __begin->template _M_node_ptr<_Node_ptr>()
+	  : _Node_ptr();
       }
 
       _Base_ptr
-      _M_end() _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
-
-      _Const_Base_ptr
       _M_end() const _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
+      { return this->_M_impl._M_header._M_base_ptr(); }
 
       static const _Key&
-      _S_key(_Const_Link_type __x)
+      _S_key(const _Node& __node)
       {
 #if __cplusplus >= 201103L
 	// If we're asking for the key we're presumably using the comparison
@@ -772,48 +1376,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 # endif // C++17
 #endif // C++11
 
-	return _KeyOfValue()(*__x->_M_valptr());
+	return _KeyOfValue()(*__node._M_valptr());
       }
 
-      static _Link_type
-      _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_left); }
-
-      static _Const_Link_type
-      _S_left(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_left); }
-
-      static _Link_type
-      _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_right); }
-
-      static _Const_Link_type
-      _S_right(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_right); }
+      static const _Key&
+      _S_key(_Base_ptr __x)
+      { return _S_key(static_cast<const _Node&>(*__x)); }
 
       static const _Key&
-      _S_key(_Const_Base_ptr __x)
-      { return _S_key(static_cast<_Const_Link_type>(__x)); }
+      _S_key(_Node_ptr __x)
+      { return _S_key(*__x); }
 
       static _Base_ptr
-      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
+      _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      { return __x->_M_left; }
 
-      static _Const_Base_ptr
-      _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
+      static _Node_ptr
+      _S_left(_Node_ptr __x)
+      {
+	return __x->_M_left
+	  ? __x->_M_left->template _M_node_ptr<_Node_ptr>()
+	  : _Node_ptr();
+      }
 
       static _Base_ptr
-      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
+      _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      { return __x->_M_right; }
 
-      static _Const_Base_ptr
-      _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
+      static _Node_ptr
+      _S_right(_Node_ptr __x) _GLIBCXX_NOEXCEPT
+      {
+	return __x->_M_right
+	  ? __x->_M_right->template _M_node_ptr<_Node_ptr>()
+	  : _Node_ptr();
+      }
 
     public:
-      typedef _Rb_tree_iterator<value_type>       iterator;
-      typedef _Rb_tree_const_iterator<value_type> const_iterator;
+      typedef typename _Node_traits::_Iterator		iterator;
+      typedef typename _Node_traits::_Const_iterator	const_iterator;
 
       typedef std::reverse_iterator<iterator>       reverse_iterator;
       typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
@@ -846,7 +1446,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_(_Base_ptr __x, _Base_ptr __y, _Arg&& __v, _NodeGen&);
 
       iterator
-      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Link_type __z);
+      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Node_ptr __z);
 
       template<typename _Arg>
 	iterator
@@ -857,10 +1457,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_equal_lower(_Arg&& __x);
 
       iterator
-      _M_insert_lower_node(_Base_ptr __p, _Link_type __z);
+      _M_insert_lower_node(_Base_ptr __p, _Node_ptr __z);
 
       iterator
-      _M_insert_equal_lower_node(_Link_type __z);
+      _M_insert_equal_lower_node(_Node_ptr __z);
 #else
       template<typename _NodeGen>
 	iterator
@@ -879,22 +1479,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       enum { __as_lvalue, __as_rvalue };
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
-	_M_copy(_Link_type, _Base_ptr, _NodeGen&);
+	_Base_ptr
+	_M_copy(_Node_ptr, _Base_ptr, _NodeGen&);
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
+	_Base_ptr
 	_M_copy(const _Rb_tree& __x, _NodeGen& __gen)
 	{
-	  _Link_type __root =
-	    _M_copy<_MoveValues>(__x._M_mbegin(), _M_end(), __gen);
-	  _M_leftmost() = _S_minimum(__root);
-	  _M_rightmost() = _S_maximum(__root);
+	  _Base_ptr __root =
+	    _M_copy<_MoveValues>(__x._M_begin_node(), _M_end(), __gen);
+	  _M_leftmost() = _Node_base::_S_minimum(__root);
+	  _M_rightmost() = _Node_base::_S_maximum(__root);
 	  _M_impl._M_node_count = __x._M_impl._M_node_count;
 	  return __root;
 	}
 
-      _Link_type
+      _Base_ptr
       _M_copy(const _Rb_tree& __x)
       {
 	_Alloc_node __an(*this);
@@ -902,22 +1502,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       void
-      _M_erase(_Link_type __x);
+      _M_erase(_Node_ptr __x);
 
-      iterator
-      _M_lower_bound(_Link_type __x, _Base_ptr __y,
-		     const _Key& __k);
-
-      const_iterator
-      _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _Base_ptr
+      _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k) const;
 
-      iterator
-      _M_upper_bound(_Link_type __x, _Base_ptr __y,
-		     const _Key& __k);
-
-      const_iterator
-      _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _Base_ptr
+      _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k) const;
 
     public:
@@ -935,7 +1527,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x)
       : _M_impl(__x._M_impl)
       {
-	if (__x._M_root() != 0)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -947,7 +1539,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x, const allocator_type& __a)
       : _M_impl(__x._M_impl._M_key_compare, _Node_allocator(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -966,7 +1558,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a, false_type)
       : _M_impl(__x._M_impl._M_key_compare, std::move(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_move_data(__x, false_type{});
       }
 
@@ -974,14 +1566,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a)
       noexcept( noexcept(
 	_Rb_tree(std::declval<_Rb_tree&&>(), std::declval<_Node_allocator&&>(),
-		 std::declval<typename _Alloc_traits::is_always_equal>())) )
+		 std::declval<typename _Node_alloc_traits::is_always_equal>())) )
       : _Rb_tree(std::move(__x), std::move(__a),
-		 typename _Alloc_traits::is_always_equal{})
+		 typename _Node_alloc_traits::is_always_equal{})
       { }
 #endif
 
       ~_Rb_tree() _GLIBCXX_NOEXCEPT
-      { _M_erase(_M_begin()); }
+      { _M_erase(_M_begin_node()); }
 
       _Rb_tree&
       operator=(const _Rb_tree& __x);
@@ -1001,11 +1593,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       end() _GLIBCXX_NOEXCEPT
-      { return iterator(&this->_M_impl._M_header); }
+      { return iterator(_M_end()); }
 
       const_iterator
       end() const _GLIBCXX_NOEXCEPT
-      { return const_iterator(&this->_M_impl._M_header); }
+      { return const_iterator(_M_end()); }
 
       reverse_iterator
       rbegin() _GLIBCXX_NOEXCEPT
@@ -1033,7 +1625,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       size_type
       max_size() const _GLIBCXX_NOEXCEPT
-      { return _Alloc_traits::max_size(_M_get_Node_allocator()); }
+      { return _Node_alloc_traits::max_size(_M_get_Node_allocator()); }
 
       void
       swap(_Rb_tree& __t)
@@ -1194,7 +1786,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	const_iterator __result = __position;
 	++__result;
 	_M_erase_aux(__position);
-	return __result._M_const_cast();
+	return iterator(__result._M_node);
       }
 
       // LWG 2059.
@@ -1235,7 +1827,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       erase(const_iterator __first, const_iterator __last)
       {
 	_M_erase_aux(__first, __last);
-	return __last._M_const_cast();
+	return iterator(__last._M_node);
       }
 #else
       void
@@ -1250,7 +1842,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       void
       clear() _GLIBCXX_NOEXCEPT
       {
-	_M_erase(_M_begin());
+	_M_erase(_M_begin_node());
 	_M_impl._M_reset();
       }
 
@@ -1266,19 +1858,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       lower_bound(const key_type& __k)
-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
+      { return iterator(_M_lower_bound(_M_begin(), _M_end(), __k)); }
 
       const_iterator
       lower_bound(const key_type& __k) const
-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
+      {
+	return const_iterator
+	  (_M_lower_bound(_M_begin(), _M_end(), __k));
+      }
 
       iterator
       upper_bound(const key_type& __k)
-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
+      { return iterator(_M_upper_bound(_M_begin(), _M_end(), __k)); }
 
       const_iterator
       upper_bound(const key_type& __k) const
-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
+      {
+	return const_iterator
+	  (_M_upper_bound(_M_begin(), _M_end(), __k));
+      }
 
       pair<iterator, iterator>
       equal_range(const key_type& __k);
@@ -1293,7 +1891,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_find_tr(const _Kt& __k)
 	{
 	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_find_tr(__k)._M_const_cast();
+	  return iterator(__const_this->_M_find_tr(__k)._M_node);
 	}
 
       template<typename _Kt,
@@ -1301,7 +1899,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	const_iterator
 	_M_find_tr(const _Kt& __k) const
 	{
-	  auto __j = _M_lower_bound_tr(__k);
+	  const_iterator __j(_M_lower_bound_tr(__k));
 	  if (__j != end() && _M_impl._M_key_compare(__k, _S_key(__j._M_node)))
 	    __j = end();
 	  return __j;
@@ -1318,21 +1916,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename _Kt,
 	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	iterator
-	_M_lower_bound_tr(const _Kt& __k)
-	{
-	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_lower_bound_tr(__k)._M_const_cast();
-	}
-
-      template<typename _Kt,
-	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	const_iterator
+	_Base_ptr
 	_M_lower_bound_tr(const _Kt& __k) const
 	{
 	  auto __x = _M_begin();
 	  auto __y = _M_end();
-	  while (__x != 0)
+	  while (__x)
 	    if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	      {
 		__y = __x;
@@ -1340,26 +1929,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      }
 	    else
 	      __x = _S_right(__x);
-	  return const_iterator(__y);
+	  return __y;
 	}
 
       template<typename _Kt,
 	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	iterator
-	_M_upper_bound_tr(const _Kt& __k)
-	{
-	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_upper_bound_tr(__k)._M_const_cast();
-	}
-
-      template<typename _Kt,
-	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	const_iterator
+	_Base_ptr
 	_M_upper_bound_tr(const _Kt& __k) const
 	{
 	  auto __x = _M_begin();
 	  auto __y = _M_end();
-	  while (__x != 0)
+	  while (__x)
 	    if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	      {
 		__y = __x;
@@ -1367,7 +1947,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      }
 	    else
 	      __x = _S_right(__x);
-	  return const_iterator(__y);
+	  return __y;
 	}
 
       template<typename _Kt,
@@ -1377,7 +1957,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  const _Rb_tree* __const_this = this;
 	  auto __ret = __const_this->_M_equal_range_tr(__k);
-	  return { __ret.first._M_const_cast(), __ret.second._M_const_cast() };
+	  return
+	    { iterator(__ret.first._M_node), iterator(__ret.second._M_node) };
 	}
 
       template<typename _Kt,
@@ -1385,7 +1966,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	pair<const_iterator, const_iterator>
 	_M_equal_range_tr(const _Kt& __k) const
 	{
-	  auto __low = _M_lower_bound_tr(__k);
+	  const_iterator __low(_M_lower_bound_tr(__k));
 	  auto __high = __low;
 	  auto& __cmp = _M_impl._M_key_compare;
 	  while (__high != end() && !__cmp(__k, _S_key(__high._M_node)))
@@ -1401,7 +1982,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #if __cplusplus >= 201103L
       _Rb_tree&
       operator=(_Rb_tree&&)
-      noexcept(_Alloc_traits::_S_nothrow_move()
+      noexcept(_Node_alloc_traits::_S_nothrow_move()
 	       && is_nothrow_move_assignable<_Compare>::value);
 
       template<typename _Iterator>
@@ -1530,10 +2111,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       node_type
       extract(const_iterator __pos)
       {
-	auto __ptr = _Rb_tree_rebalance_for_erase(
-	    __pos._M_const_cast()._M_node, _M_impl._M_header);
+	auto __ptr = _Node_traits::_S_rebalance_for_erase
+	  (__pos._M_node, _M_impl._M_header);
 	--_M_impl._M_node_count;
-	return { static_cast<_Link_type>(__ptr), _M_get_Node_allocator() };
+	return
+	  { __ptr->template _M_node_ptr<_Node_ptr>(), _M_get_Node_allocator() };
       }
 
       /// Extract a node.
@@ -1567,11 +2149,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr = _Node_traits::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
 		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+				 __ptr->template _M_node_ptr<_Node_ptr>());
 		}
 	    }
 	}
@@ -1589,11 +2171,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr = _Node_traits::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
 		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+				 __ptr->template _M_node_ptr<_Node_ptr>());
 		}
 	    }
 	}
@@ -1666,7 +2248,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
 	_Rb_tree& _M_t;
-	_Link_type _M_node;
+	_Node_ptr _M_node;
       };
 #endif // C++11
     };
@@ -1704,7 +2286,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_move_assign(_Rb_tree& __x, true_type)
     {
       clear();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	_M_move_data(__x, true_type());
       std::__alloc_on_move(_M_get_Node_allocator(),
 			   __x._M_get_Node_allocator());
@@ -1723,7 +2305,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // structure.
       _Reuse_or_alloc_node __roan(*this);
       _M_impl._M_reset();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	{
 	  _M_root() = _M_copy<__as_rvalue>(__x, __roan);
 	  __x.clear();
@@ -1735,11 +2317,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     operator=(_Rb_tree&& __x)
-    noexcept(_Alloc_traits::_S_nothrow_move()
+    noexcept(_Node_alloc_traits::_S_nothrow_move()
 	     && is_nothrow_move_assignable<_Compare>::value)
     {
       _M_impl._M_key_compare = std::move(__x._M_impl._M_key_compare);
-      _M_move_assign(__x, __bool_constant<_Alloc_traits::_S_nothrow_move()>());
+      _M_move_assign(__x,
+		     __bool_constant<_Node_alloc_traits::_S_nothrow_move()>());
       return *this;
     }
 
@@ -1780,11 +2363,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  // Note that _Key may be a constant type.
 #if __cplusplus >= 201103L
-	  if (_Alloc_traits::_S_propagate_on_copy_assign())
+	  if (_Node_alloc_traits::_S_propagate_on_copy_assign())
 	    {
 	      auto& __this_alloc = this->_M_get_Node_allocator();
 	      auto& __that_alloc = __x._M_get_Node_allocator();
-	      if (!_Alloc_traits::_S_always_equal()
+	      if (!_Node_alloc_traits::_S_always_equal()
 		  && __this_alloc != __that_alloc)
 		{
 		  // Replacement allocator cannot free existing storage, we need
@@ -1798,7 +2381,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Reuse_or_alloc_node __roan(*this);
 	  _M_impl._M_reset();
 	  _M_impl._M_key_compare = __x._M_impl._M_key_compare;
-	  if (__x._M_root() != 0)
+	  if (__x._M_root())
 	    _M_root() = _M_copy<__as_lvalue>(__x, __roan);
 	}
 
@@ -1822,14 +2405,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 		 _NodeGen& __node_gen)
       {
-	bool __insert_left = (__x != 0 || __p == _M_end()
+	bool __insert_left = (__x || __p == _M_end()
 			      || _M_impl._M_key_compare(_KeyOfValue()(__v),
 							_S_key(__p)));
 
-	_Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
+	_Base_ptr __z =
+	  __node_gen(_GLIBCXX_FORWARD(_Arg, __v))->_M_base_ptr();
 
-	_Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				      this->_M_impl._M_header);
+	_Node_traits::_S_insert_and_rebalance
+	  (__insert_left, __z, __p, this->_M_impl._M_header);
 	++_M_impl._M_node_count;
 	return iterator(__z);
       }
@@ -1851,10 +2435,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _KeyOfValue()(__v)));
 
-      _Link_type __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
-
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Base_ptr __z =
+	_M_create_node(_GLIBCXX_FORWARD(_Arg, __v))->_M_base_ptr();
+      _Node_traits::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -1872,9 +2456,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_insert_equal_lower(const _Val& __v)
 #endif
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = !_M_impl._M_key_compare(_S_key(__x), _KeyOfValue()(__v)) ?
@@ -1886,25 +2470,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Key, typename _Val, typename _KoV,
 	   typename _Compare, typename _Alloc>
     template<bool _MoveValues, typename _NodeGen>
-      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Link_type
+      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Base_ptr
       _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::
-      _M_copy(_Link_type __x, _Base_ptr __p, _NodeGen& __node_gen)
+      _M_copy(_Node_ptr __x, _Base_ptr __p, _NodeGen& __node_gen)
       {
 	// Structural copy. __x and __p must be non-null.
-	_Link_type __top = _M_clone_node<_MoveValues>(__x, __node_gen);
+	_Node_ptr __top = _M_clone_node<_MoveValues>(__x, __node_gen);
+	_Base_ptr __top_base = __top->_M_base_ptr();
 	__top->_M_parent = __p;
 
 	__try
 	  {
 	    if (__x->_M_right)
 	      __top->_M_right =
-		_M_copy<_MoveValues>(_S_right(__x), __top, __node_gen);
-	    __p = __top;
+		_M_copy<_MoveValues>(_S_right(__x), __top_base, __node_gen);
+	    __p = __top_base;
 	    __x = _S_left(__x);
 
-	    while (__x != 0)
+	    while (__x)
 	      {
-		_Link_type __y = _M_clone_node<_MoveValues>(__x, __node_gen);
+		_Base_ptr __y =
+		  _M_clone_node<_MoveValues>(__x, __node_gen)->_M_base_ptr();
 		__p->_M_left = __y;
 		__y->_M_parent = __p;
 		if (__x->_M_right)
@@ -1919,20 +2505,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    _M_erase(__top);
 	    __throw_exception_again;
 	  }
-	return __top;
+	return __top_base;
       }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     void
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_erase(_Link_type __x)
+    _M_erase(_Node_ptr __x)
     {
       // Erase without rebalancing.
-      while (__x != 0)
+      while (__x)
 	{
 	  _M_erase(_S_right(__x));
-	  _Link_type __y = _S_left(__x);
+	  _Node_ptr __y = _S_left(__x);
 	  _M_drop_node(__x);
 	  __x = __y;
 	}
@@ -1941,65 +2527,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::iterator
+		      _Compare, _Alloc>::_Base_ptr
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Link_type __x, _Base_ptr __y,
-		   const _Key& __k)
-    {
-      while (__x != 0)
-	if (!_M_impl._M_key_compare(_S_key(__x), __k))
-	  __y = __x, __x = _S_left(__x);
-	else
-	  __x = _S_right(__x);
-      return iterator(__y);
-    }
-
-  template<typename _Key, typename _Val, typename _KeyOfValue,
-	   typename _Compare, typename _Alloc>
-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::const_iterator
-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	  __y = __x, __x = _S_left(__x);
 	else
 	  __x = _S_right(__x);
-      return const_iterator(__y);
-    }
-
-  template<typename _Key, typename _Val, typename _KeyOfValue,
-	   typename _Compare, typename _Alloc>
-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::iterator
-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Link_type __x, _Base_ptr __y,
-		   const _Key& __k)
-    {
-      while (__x != 0)
-	if (_M_impl._M_key_compare(__k, _S_key(__x)))
-	  __y = __x, __x = _S_left(__x);
-	else
-	  __x = _S_right(__x);
-      return iterator(__y);
+      return __y;
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::const_iterator
+		      _Compare, _Alloc>::_Base_ptr
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	  __y = __x, __x = _S_left(__x);
 	else
 	  __x = _S_right(__x);
-      return const_iterator(__y);
+      return __y;
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2011,9 +2565,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k)
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
 	    __x = _S_right(__x);
@@ -2021,13 +2575,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Link_type __xu(__x);
+	      _Base_ptr __xu(__x);
 	      _Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
-	      return pair<iterator,
-			  iterator>(_M_lower_bound(__x, __y, __k),
-				    _M_upper_bound(__xu, __yu, __k));
+	      return make_pair(iterator(_M_lower_bound(__x, __y, __k)),
+			       iterator(_M_upper_bound(__xu, __yu, __k)));
 	    }
 	}
       return pair<iterator, iterator>(iterator(__y),
@@ -2043,9 +2596,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k) const
     {
-      _Const_Link_type __x = _M_begin();
-      _Const_Base_ptr __y = _M_end();
-      while (__x != 0)
+      _Base_ptr __x = _M_begin();
+      _Base_ptr __y = _M_end();
+      while (__x)
 	{
 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
 	    __x = _S_right(__x);
@@ -2053,13 +2606,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Const_Link_type __xu(__x);
-	      _Const_Base_ptr __yu(__y);
+	      _Base_ptr __xu(__x);
+	      _Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
-	      return pair<const_iterator,
-			  const_iterator>(_M_lower_bound(__x, __y, __k),
-					  _M_upper_bound(__xu, __yu, __k));
+	      return make_pair(const_iterator(_M_lower_bound(__x, __y, __k)),
+			       const_iterator(_M_upper_bound(__xu, __yu, __k)));
 	    }
 	}
       return pair<const_iterator, const_iterator>(const_iterator(__y),
@@ -2073,12 +2625,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     swap(_Rb_tree& __t)
     _GLIBCXX_NOEXCEPT_IF(__is_nothrow_swappable<_Compare>::value)
     {
-      if (_M_root() == 0)
+      if (!_M_root())
 	{
-	  if (__t._M_root() != 0)
+	  if (__t._M_root())
 	    _M_impl._M_move_data(__t._M_impl);
 	}
-      else if (__t._M_root() == 0)
+      else if (!__t._M_root())
 	__t._M_impl._M_move_data(_M_impl);
       else
 	{
@@ -2093,8 +2645,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // No need to swap header's color as it does not change.
       std::swap(this->_M_impl._M_key_compare, __t._M_impl._M_key_compare);
 
-      _Alloc_traits::_S_on_swap(_M_get_Node_allocator(),
-				__t._M_get_Node_allocator());
+      _Node_alloc_traits::_S_on_swap(_M_get_Node_allocator(),
+				     __t._M_get_Node_allocator());
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2107,10 +2659,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_unique_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       bool __comp = true;
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __comp = _M_impl._M_key_compare(__k, _S_key(__x));
@@ -2126,7 +2678,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
       if (_M_impl._M_key_compare(_S_key(__j._M_node), __k))
 	return _Res(__x, __y);
-      return _Res(__j._M_node, 0);
+      return _Res(__j._M_node, _Base_ptr());
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2139,9 +2691,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_equal_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = _M_impl._M_key_compare(__k, _S_key(__x)) ?
@@ -2209,44 +2761,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_hint_unique_pos(const_iterator __position,
 				  const key_type& __k)
     {
-      iterator __pos = __position._M_const_cast();
       typedef pair<_Base_ptr, _Base_ptr> _Res;
 
       // end()
-      if (__pos._M_node == _M_end())
+      if (__position._M_node == _M_end())
 	{
 	  if (size() > 0
 	      && _M_impl._M_key_compare(_S_key(_M_rightmost()), __k))
-	    return _Res(0, _M_rightmost());
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else
 	    return _M_get_insert_unique_pos(__k);
 	}
-      else if (_M_impl._M_key_compare(__k, _S_key(__pos._M_node)))
+      else if (_M_impl._M_key_compare(__k, _S_key(__position._M_node)))
 	{
 	  // First, try before...
-	  iterator __before = __pos;
-	  if (__pos._M_node == _M_leftmost()) // begin()
+	  iterator __before(__position._M_node);
+	  if (__position._M_node == _M_leftmost()) // begin()
 	    return _Res(_M_leftmost(), _M_leftmost());
 	  else if (_M_impl._M_key_compare(_S_key((--__before)._M_node), __k))
 	    {
-	      if (_S_right(__before._M_node) == 0)
-		return _Res(0, __before._M_node);
+	      if (!_S_right(__before._M_node))
+		return _Res(_Base_ptr(), __before._M_node);
 	      else
-		return _Res(__pos._M_node, __pos._M_node);
+		return _Res(__position._M_node, __position._M_node);
 	    }
 	  else
 	    return _M_get_insert_unique_pos(__k);
 	}
-      else if (_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
+      else if (_M_impl._M_key_compare(_S_key(__position._M_node), __k))
 	{
 	  // ... then try after.
-	  iterator __after = __pos;
-	  if (__pos._M_node == _M_rightmost())
-	    return _Res(0, _M_rightmost());
+	  iterator __after(__position._M_node);
+	  if (__position._M_node == _M_rightmost())
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else if (_M_impl._M_key_compare(__k, _S_key((++__after)._M_node)))
 	    {
-	      if (_S_right(__pos._M_node) == 0)
-		return _Res(0, __pos._M_node);
+	      if (!_S_right(__position._M_node))
+		return _Res(_Base_ptr(), __position._M_node);
 	      else
 		return _Res(__after._M_node, __after._M_node);
 	    }
@@ -2255,7 +2806,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
       else
 	// Equivalent keys.
-	return _Res(__pos._M_node, 0);
+	return _Res(__position._M_node, _Base_ptr());
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2294,30 +2845,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
     {
-      iterator __pos = __position._M_const_cast();
       typedef pair<_Base_ptr, _Base_ptr> _Res;
 
       // end()
-      if (__pos._M_node == _M_end())
+      if (__position._M_node == _M_end())
 	{
 	  if (size() > 0
 	      && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
-	    return _Res(0, _M_rightmost());
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else
 	    return _M_get_insert_equal_pos(__k);
 	}
-      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
+      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
 	{
 	  // First, try before...
-	  iterator __before = __pos;
-	  if (__pos._M_node == _M_leftmost()) // begin()
+	  iterator __before(__position._M_node);
+	  if (__position._M_node == _M_leftmost()) // begin()
 	    return _Res(_M_leftmost(), _M_leftmost());
 	  else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
 	    {
-	      if (_S_right(__before._M_node) == 0)
-		return _Res(0, __before._M_node);
+	      if (!_S_right(__before._M_node))
+		return _Res(_Base_ptr(), __before._M_node);
 	      else
-		return _Res(__pos._M_node, __pos._M_node);
+		return _Res(__position._M_node, __position._M_node);
 	    }
 	  else
 	    return _M_get_insert_equal_pos(__k);
@@ -2325,18 +2875,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       else
 	{
 	  // ... then try after.
-	  iterator __after = __pos;
-	  if (__pos._M_node == _M_rightmost())
-	    return _Res(0, _M_rightmost());
+	  iterator __after(__position._M_node);
+	  if (__position._M_node == _M_rightmost())
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else if (!_M_impl._M_key_compare(_S_key((++__after)._M_node), __k))
 	    {
-	      if (_S_right(__pos._M_node) == 0)
-		return _Res(0, __pos._M_node);
+	      if (!_S_right(__position._M_node))
+		return _Res(_Base_ptr(), __position._M_node);
 	      else
 		return _Res(__after._M_node, __after._M_node);
 	    }
 	  else
-	    return _Res(0, 0);
+	    return _Res(_Base_ptr(), _Base_ptr());
 	}
     }
 
@@ -2373,46 +2923,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Link_type __z)
+    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Node_ptr __z)
     -> iterator
     {
-      bool __insert_left = (__x != 0 || __p == _M_end()
+      bool __insert_left = (__x || __p == _M_end()
 			    || _M_impl._M_key_compare(_S_key(__z),
 						      _S_key(__p)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Base_ptr __base_z = __z->_M_base_ptr();
+      _Node_traits::_S_insert_and_rebalance
+	(__insert_left, __base_z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
-      return iterator(__z);
+      return iterator(__base_z);
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_lower_node(_Base_ptr __p, _Link_type __z)
+    _M_insert_lower_node(_Base_ptr __p, _Node_ptr __z)
     -> iterator
     {
       bool __insert_left = (__p == _M_end()
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _S_key(__z)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Base_ptr __base_z = __z->_M_base_ptr();
+      _Node_traits::_S_insert_and_rebalance
+	(__insert_left, __base_z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
-      return iterator(__z);
+      return iterator(__base_z);
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_equal_lower_node(_Link_type __z)
+    _M_insert_equal_lower_node(_Node_ptr __z)
     -> iterator
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = !_M_impl._M_key_compare(_S_key(__x), _S_key(__z)) ?
@@ -2487,11 +3039,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     _M_erase_aux(const_iterator __position)
     {
-      _Link_type __y =
-	static_cast<_Link_type>(_Rb_tree_rebalance_for_erase
-				(const_cast<_Base_ptr>(__position._M_node),
-				 this->_M_impl._M_header));
-      _M_drop_node(__y);
+      _Base_ptr __y = _Node_traits::_S_rebalance_for_erase
+	(__position._M_node, this->_M_impl._M_header);
+      _M_drop_node(__y->template _M_node_ptr<_Node_ptr>());
       --_M_impl._M_node_count;
     }
 
@@ -2527,7 +3077,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     find(const _Key& __k)
     {
-      iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
+      iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
       return (__j == end()
 	      || _M_impl._M_key_compare(__k,
 					_S_key(__j._M_node))) ? end() : __j;
@@ -2540,7 +3090,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     find(const _Key& __k) const
     {
-      const_iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
+      const_iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
       return (__j == end()
 	      || _M_impl._M_key_compare(__k,
 					_S_key(__j._M_node))) ? end() : __j;
@@ -2574,9 +3124,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       unsigned int __len = _Rb_tree_black_count(_M_leftmost(), _M_root());
       for (const_iterator __it = begin(); __it != end(); ++__it)
 	{
-	  _Const_Link_type __x = static_cast<_Const_Link_type>(__it._M_node);
-	  _Const_Link_type __L = _S_left(__x);
-	  _Const_Link_type __R = _S_right(__x);
+	  _Base_ptr __x = __it._M_node;
+	  _Base_ptr __L = _S_left(__x);
+	  _Base_ptr __R = _S_right(__x);
 
 	  if (__x->_M_color == _S_red)
 	    if ((__L && __L->_M_color == _S_red)
@@ -2592,9 +3142,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    return false;
 	}
 
-      if (_M_leftmost() != _Rb_tree_node_base::_S_minimum(_M_root()))
+      if (_M_leftmost() != _Node_base::_S_minimum(_M_root()))
 	return false;
-      if (_M_rightmost() != _Rb_tree_node_base::_S_maximum(_M_root()))
+      if (_M_rightmost() != _Node_base::_S_maximum(_M_root()))
 	return false;
       return true;
     }
diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..3d83738f86b
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
@@ -0,0 +1,37 @@
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::map<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::map<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( v.begin()->second == 0 );
+  VERIFY( ++v.begin() == v.end() );
+  v.insert({ T { 1 }, 1 });
+  VERIFY( v.size() == 2 );
+  VERIFY( v.find(T()) != v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..41689ef43b7
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
@@ -0,0 +1,33 @@
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multimap<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::multimap<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..589e6b0a617
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
@@ -0,0 +1,32 @@
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multiset<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::multiset<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..bbcf32b20be
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
@@ -0,0 +1,32 @@
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::set<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::set<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
  
François Dumont Dec. 14, 2024, 1:35 p.m. UTC | #20
Hi

Here is an update with the alloc ptr specific tests and so fixes.

PR updated:

https://forge.sourceware.org/gcc/gcc-TEST/pulls/27

François


On 09/12/2024 07:05, François Dumont wrote:
>
> On 04/12/2024 22:48, Jonathan Wakely wrote:
>> On 04/12/24 19:27 +0100, François Dumont wrote:
>>> Hi
>>>
>>> I've completed the synchronization with your equivalent PR for 
>>> std::list so here is the updated patch.
>>>
>>> PR updated a couple of days ago.
>>>
>>> Note that I've started to rework the patch for the same in _Hashtable.
>>
>> Great, thanks.
>>
>>>
>>> #if __cplusplus < 201103L
>>>       void
>>> -      _M_construct_node(_Link_type __node, const value_type& __x)
>>> +      _M_construct_node(_Base_ptr __p, const value_type& __x)
>>
>> And can this be left as _Node_ptr instead of changed to _Base_ptr?
>> Then it wouldn't need the static_cast, and the callers wouldn't need
>> to use _M_base_ptr().
>>
>> Logically, it has to be a _Node_ptr or the cast isn't valid anyway.
>>
>> There seem to be several places like this where we're giving up
>> valuable type information by using _base_ptr instead of _Node_ptr.
>>
>> Is that really necessary? If we have a _Node_ptr we can always get a
>> _Base_ptr easily by calling _M_base_ptr().
>>
>> Is it a constness thing, because _M_begin() const was changed to
>> return _Base_ptr instead of _Const_link_ptr?
>>
> No, just me being lazy and considering that as we store _Base_ptr we 
> can also use it everywhere.
>
> In this new version I've preserved _Node_ptr almost everywhere.
>
> But _M_begin() is still returning a _Base_ptr and I've introduced 
> _M_begin_node() for rare occasions when a _Node_ptr is needed.
>
> Note that I've hesitated to add a _S_nullptr() to get a _Base_ptr null 
> pointer. Is default constructor on fancy pointer type mandatory ? I 
> fear that only constructor from nullptr is mandatory.
>
> I've forced push on:
>
> https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
>
> François
>
diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
index bc27e191e8b..c87591757e8 100644
--- a/libstdc++-v3/include/bits/stl_tree.h
+++ b/libstdc++-v3/include/bits/stl_tree.h
@@ -66,6 +66,7 @@
 #include <bits/allocator.h>
 #include <bits/stl_function.h>
 #include <bits/cpp_type_traits.h>
+#include <bits/ptr_traits.h>
 #include <ext/alloc_traits.h>
 #if __cplusplus >= 201103L
 # include <ext/aligned_buffer.h>
@@ -74,6 +75,13 @@
 # include <bits/node_handle.h>
 #endif
 
+#if __cplusplus < 201103L
+# undef _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+# define _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE 0
+#elif ! defined _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+# define _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE 1
+#endif
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -99,7 +107,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   struct _Rb_tree_node_base
   {
     typedef _Rb_tree_node_base* _Base_ptr;
-    typedef const _Rb_tree_node_base* _Const_Base_ptr;
 
     _Rb_tree_color	_M_color;
     _Base_ptr		_M_parent;
@@ -113,13 +120,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return __x;
     }
 
-    static _Const_Base_ptr
-    _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-    {
-      while (__x->_M_left != 0) __x = __x->_M_left;
-      return __x;
-    }
-
     static _Base_ptr
     _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
     {
@@ -127,12 +127,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return __x;
     }
 
-    static _Const_Base_ptr
-    _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-    {
-      while (__x->_M_right != 0) __x = __x->_M_right;
-      return __x;
-    }
+    // This is not const-correct, but it's only used in a const access path
+    // by std::_Rb_tree::_M_end() where the pointer is used to initialize a
+    // const_iterator and so constness is restored.
+    _Base_ptr
+    _M_base_ptr() const _GLIBCXX_NOEXCEPT
+    { return const_cast<_Rb_tree_node_base*>(this); }
   };
 
   // Helper type offering value initialization guarantee on the compare functor.
@@ -213,8 +213,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Val>
     struct _Rb_tree_node : public _Rb_tree_node_base
     {
-      typedef _Rb_tree_node<_Val>* _Link_type;
-
 #if __cplusplus < 201103L
       _Val _M_value_field;
 
@@ -236,20 +234,140 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_valptr() const
       { return _M_storage._M_ptr(); }
 #endif
+
+      _Rb_tree_node*
+      _M_node_ptr() _GLIBCXX_NOEXCEPT
+      { return this; }
+    };
+
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+namespace __rb_tree
+{
+  template<typename _VoidPtr>
+    struct _Node_base
+    {
+      using _Base_ptr = __ptr_rebind<_VoidPtr, _Node_base>;
+
+      _Rb_tree_color	_M_color;
+      _Base_ptr		_M_parent;
+      _Base_ptr		_M_left;
+      _Base_ptr		_M_right;
+
+      static _Base_ptr
+      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      {
+	while (__x->_M_left) __x = __x->_M_left;
+	return __x;
+      }
+
+      static _Base_ptr
+      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      {
+	while (__x->_M_right) __x = __x->_M_right;
+	return __x;
+      }
+
+      // This is not const-correct, but it's only used in a const access path
+      // by std::_Rb_tree::_M_end() where the pointer is used to initialize a
+      // const_iterator and so constness is restored.
+      _Base_ptr
+      _M_base_ptr() const noexcept
+      {
+	return pointer_traits<_Base_ptr>::pointer_to
+	  (*const_cast<_Node_base*>(this));
+      }
+    };
+
+  // Helper type to manage default initialization of node count and header.
+  template<typename _NodeBase>
+    struct _Header
+    {
+    private:
+      using _Base_ptr =  typename _NodeBase::_Base_ptr;
+
+    public:
+      _NodeBase		_M_header;
+      size_t		_M_node_count; // Keeps track of size of tree.
+
+      _Header() noexcept
+      {
+	_M_header._M_color = _S_red;
+	_M_reset();
+      }
+
+      _Header(_Header&& __x) noexcept
+      {
+	if (__x._M_header._M_parent)
+	  _M_move_data(__x);
+	else
+	  {
+	    _M_header._M_color = _S_red;
+	    _M_reset();
+	  }
+      }
+
+      void
+      _M_move_data(_Header& __from)
+      {
+	_M_header._M_color = __from._M_header._M_color;
+	_M_header._M_parent = __from._M_header._M_parent;
+	_M_header._M_left = __from._M_header._M_left;
+	_M_header._M_right = __from._M_header._M_right;
+	_M_header._M_parent->_M_parent = _M_header._M_base_ptr();
+	_M_node_count = __from._M_node_count;
+
+	__from._M_reset();
+      }
+
+      void
+      _M_reset()
+      {
+	_M_header._M_parent = nullptr;
+	_M_header._M_left = _M_header._M_right = _M_header._M_base_ptr();
+	_M_node_count = 0;
+      }
     };
 
+  template<typename _ValPtr>
+    struct _Node : public __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>
+    {
+      using value_type = typename pointer_traits<_ValPtr>::element_type;
+      using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
+
+      _Node() noexcept { }
+      ~_Node() { }
+      _Node(_Node&&) = delete;
+
+      union _Uninit_storage
+      {
+	_Uninit_storage() noexcept { }
+	~_Uninit_storage() { }
+
+	value_type _M_data;
+      };
+      _Uninit_storage _M_u;
+
+      value_type*
+      _M_valptr()
+      { return std::addressof(_M_u._M_data); }
+
+      value_type const*
+      _M_valptr() const
+      { return std::addressof(_M_u._M_data); }
+
+      _Node_ptr
+      _M_node_ptr() noexcept
+      { return pointer_traits<_Node_ptr>::pointer_to(*this); }
+    };
+} // namespace __rb_tree
+#endif // _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+
   _GLIBCXX_PURE _Rb_tree_node_base*
   _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
 
-  _GLIBCXX_PURE const _Rb_tree_node_base*
-  _Rb_tree_increment(const _Rb_tree_node_base* __x) throw ();
-
   _GLIBCXX_PURE _Rb_tree_node_base*
   _Rb_tree_decrement(_Rb_tree_node_base* __x) throw ();
 
-  _GLIBCXX_PURE const _Rb_tree_node_base*
-  _Rb_tree_decrement(const _Rb_tree_node_base* __x) throw ();
-
   template<typename _Tp>
     struct _Rb_tree_iterator
     {
@@ -260,9 +378,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef bidirectional_iterator_tag iterator_category;
       typedef ptrdiff_t			 difference_type;
 
-      typedef _Rb_tree_iterator<_Tp>		_Self;
       typedef _Rb_tree_node_base::_Base_ptr	_Base_ptr;
-      typedef _Rb_tree_node<_Tp>*		_Link_type;
+      typedef _Rb_tree_node<_Tp>*		_Node_ptr;
 
       _Rb_tree_iterator() _GLIBCXX_NOEXCEPT
       : _M_node() { }
@@ -273,49 +390,51 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       reference
       operator*() const _GLIBCXX_NOEXCEPT
-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type> (_M_node)->_M_valptr(); }
+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
-      _Self&
+      _Rb_tree_iterator&
       operator++() _GLIBCXX_NOEXCEPT
       {
 	_M_node = _Rb_tree_increment(_M_node);
 	return *this;
       }
 
-      _Self
+      _Rb_tree_iterator
       operator++(int) _GLIBCXX_NOEXCEPT
       {
-	_Self __tmp = *this;
+	_Rb_tree_iterator __tmp = *this;
 	_M_node = _Rb_tree_increment(_M_node);
 	return __tmp;
       }
 
-      _Self&
+      _Rb_tree_iterator&
       operator--() _GLIBCXX_NOEXCEPT
       {
 	_M_node = _Rb_tree_decrement(_M_node);
 	return *this;
       }
 
-      _Self
+      _Rb_tree_iterator
       operator--(int) _GLIBCXX_NOEXCEPT
       {
-	_Self __tmp = *this;
+	_Rb_tree_iterator __tmp = *this;
 	_M_node = _Rb_tree_decrement(_M_node);
 	return __tmp;
       }
 
       friend bool
-      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      operator==(const _Rb_tree_iterator& __x,
+		 const _Rb_tree_iterator& __y) _GLIBCXX_NOEXCEPT
       { return __x._M_node == __y._M_node; }
 
 #if ! __cpp_lib_three_way_comparison
       friend bool
-      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      operator!=(const _Rb_tree_iterator& __x,
+		 const _Rb_tree_iterator& __y) _GLIBCXX_NOEXCEPT
       { return __x._M_node != __y._M_node; }
 #endif
 
@@ -334,9 +453,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef bidirectional_iterator_tag iterator_category;
       typedef ptrdiff_t			 difference_type;
 
-      typedef _Rb_tree_const_iterator<_Tp>		_Self;
-      typedef _Rb_tree_node_base::_Const_Base_ptr	_Base_ptr;
-      typedef const _Rb_tree_node<_Tp>*			_Link_type;
+      typedef _Rb_tree_node_base::_Base_ptr	_Base_ptr;
+      typedef const _Rb_tree_node<_Tp>*		_Node_ptr;
 
       _Rb_tree_const_iterator() _GLIBCXX_NOEXCEPT
       : _M_node() { }
@@ -348,55 +466,53 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree_const_iterator(const iterator& __it) _GLIBCXX_NOEXCEPT
       : _M_node(__it._M_node) { }
 
-      iterator
-      _M_const_cast() const _GLIBCXX_NOEXCEPT
-      { return iterator(const_cast<typename iterator::_Base_ptr>(_M_node)); }
-
       reference
       operator*() const _GLIBCXX_NOEXCEPT
-      { return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return *static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(_M_node)->_M_valptr(); }
+      { return static_cast<_Node_ptr>(_M_node)->_M_valptr(); }
 
-      _Self&
+      _Rb_tree_const_iterator&
       operator++() _GLIBCXX_NOEXCEPT
       {
 	_M_node = _Rb_tree_increment(_M_node);
 	return *this;
       }
 
-      _Self
+      _Rb_tree_const_iterator
       operator++(int) _GLIBCXX_NOEXCEPT
       {
-	_Self __tmp = *this;
+	_Rb_tree_const_iterator __tmp = *this;
 	_M_node = _Rb_tree_increment(_M_node);
 	return __tmp;
       }
 
-      _Self&
+      _Rb_tree_const_iterator&
       operator--() _GLIBCXX_NOEXCEPT
       {
 	_M_node = _Rb_tree_decrement(_M_node);
 	return *this;
       }
 
-      _Self
+      _Rb_tree_const_iterator
       operator--(int) _GLIBCXX_NOEXCEPT
       {
-	_Self __tmp = *this;
+	_Rb_tree_const_iterator __tmp = *this;
 	_M_node = _Rb_tree_decrement(_M_node);
 	return __tmp;
       }
 
       friend bool
-      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      operator==(const _Rb_tree_const_iterator& __x,
+		 const _Rb_tree_const_iterator& __y) _GLIBCXX_NOEXCEPT
       { return __x._M_node == __y._M_node; }
 
 #if ! __cpp_lib_three_way_comparison
       friend bool
-      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      operator!=(const _Rb_tree_const_iterator& __x,
+		 const _Rb_tree_const_iterator& __y) _GLIBCXX_NOEXCEPT
       { return __x._M_node != __y._M_node; }
 #endif
 
@@ -415,6 +531,485 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
 			       _Rb_tree_node_base& __header) throw ();
 
+namespace __rb_tree
+{
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+  template<bool _Const, typename _ValPtr>
+    struct _Iterator
+    {
+      template<typename _Tp>
+	using __maybe_const = __conditional_t<_Const, const _Tp, _Tp>;
+
+      using __ptr_traits =	pointer_traits<_ValPtr>;
+      using value_type =	typename __ptr_traits::element_type;
+      using reference =		__maybe_const<value_type>&;
+      using pointer =		__maybe_const<value_type>*;
+
+      using iterator_category =	bidirectional_iterator_tag;
+      using difference_type =	ptrdiff_t;
+
+      using _Node = __rb_tree::_Node<_ValPtr>;
+      using _Node_base = __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>;
+      using _Base_ptr =	 typename _Node_base::_Base_ptr;
+
+      _Iterator() noexcept
+      : _M_node() { }
+
+      constexpr explicit
+      _Iterator(_Base_ptr __x) noexcept
+      : _M_node(__x) { }
+
+      _Iterator(const _Iterator&) = default;
+      _Iterator& operator=(const _Iterator&) = default;
+
+#ifdef __glibcxx_concepts
+      constexpr
+      _Iterator(const _Iterator<false, _ValPtr>& __it) requires _Const
+#else
+      template<bool _OtherConst,
+	       typename = __enable_if_t<_Const && !_OtherConst>>
+	constexpr
+	_Iterator(const _Iterator<_OtherConst, _ValPtr>& __it)
+#endif
+	: _M_node(__it._M_node) { }
+
+      [[__nodiscard__]]
+      reference
+      operator*() const noexcept
+      { return *static_cast<_Node&>(*_M_node)._M_valptr(); }
+
+      [[__nodiscard__]]
+      pointer
+      operator->() const noexcept
+      { return static_cast<_Node&>(*_M_node)._M_valptr(); }
+
+      _GLIBCXX14_CONSTEXPR _Iterator&
+      operator++() noexcept
+      {
+	if (_M_node->_M_right)
+	  {
+	    _M_node = _M_node->_M_right;
+	    while (_M_node->_M_left)
+	      _M_node = _M_node->_M_left;
+	  }
+	else
+	  {
+	    _Base_ptr __y = _M_node->_M_parent;
+	    while (_M_node == __y->_M_right)
+	      {
+		_M_node = __y;
+		__y = __y->_M_parent;
+	      }
+	    if (_M_node->_M_right != __y)
+	      _M_node = __y;
+	  }
+
+	return *this;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator
+      operator++(int) noexcept
+      {
+	_Iterator __tmp(this->_M_node);
+	++*this;
+	return __tmp;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator&
+      operator--() noexcept
+      {
+	if (_M_node->_M_color == _S_red
+	    && _M_node->_M_parent->_M_parent == _M_node)
+	  _M_node = _M_node->_M_right;
+	else if (_M_node->_M_left)
+	  {
+	    _Base_ptr __y = _M_node->_M_left;
+	    while (__y->_M_right)
+	      __y = __y->_M_right;
+	    _M_node = __y;
+	  }
+	else
+	  {
+	    _Base_ptr __y = _M_node->_M_parent;
+	    while (_M_node == __y->_M_left)
+	      {
+		_M_node = __y;
+		__y = __y->_M_parent;
+	      }
+	    _M_node = __y;
+	  }
+	return *this;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator
+      operator--(int) noexcept
+      {
+	_Iterator __tmp(this->_M_node);
+	--*this;
+	return __tmp;
+      }
+
+      [[__nodiscard__]]
+      friend bool
+      operator==(const _Iterator& __x, const _Iterator& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node == __y._M_node; }
+
+#if ! __cpp_lib_three_way_comparison
+      [[__nodiscard__]]
+      friend bool
+      operator!=(const _Iterator& __x, const _Iterator& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node != __y._M_node; }
+#endif
+
+      _Base_ptr _M_node;
+    };
+#endif // USE_ALLOC_PTR_FOR_RB_TREE
+
+  // Determine the node and iterator types used by std::_Rb_tree.
+  template<typename _Val, typename _Ptr>
+    struct _Node_traits;
+
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE <= 9000
+  // Specialization for the simple case where the allocator's pointer type
+  // is the same type as value_type*.
+  // For ABI compatibility we can't change the types used for this case.
+  template<typename _Val>
+    struct _Node_traits<_Val, _Val*>
+    {
+      typedef _Rb_tree_node<_Val>		_Node;
+      typedef _Node*				_Node_ptr;
+      typedef _Rb_tree_node_base		_Node_base;
+      typedef _Node_base*			_Base_ptr;
+      typedef _Rb_tree_header			_Header_t;
+      typedef _Rb_tree_iterator<_Val>		_Iterator;
+      typedef _Rb_tree_const_iterator<_Val>	_Const_iterator;
+
+      __attribute__((__nonnull__))
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _Node_base* __x, _Node_base* __p,
+			      _Node_base& __header) _GLIBCXX_USE_NOEXCEPT
+      {
+	return _Rb_tree_insert_and_rebalance(__insert_left, __x, __p, __header);
+      }
+
+      __attribute__((__nonnull__,__returns_nonnull__))
+      static _Node_base*
+      _S_rebalance_for_erase(_Node_base* const __z,
+			     _Node_base& __header) _GLIBCXX_USE_NOEXCEPT
+      { return _Rb_tree_rebalance_for_erase(__z, __header); }
+    };
+#endif
+
+#if ! _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+  // Always use the T* specialization.
+  template<typename _Val, typename _Ptr>
+    struct _Node_traits
+    : _Node_traits<_Val, _Val*>
+    { };
+#else
+  // Primary template used when the allocator uses fancy pointers.
+  template<typename _Val, typename _ValPtr>
+    struct _Node_traits
+    {
+      using _Node = __rb_tree::_Node<_ValPtr>;
+      using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
+      using _Node_base = __rb_tree::_Node_base<__ptr_rebind<_ValPtr, void>>;
+      using _Base_ptr = __ptr_rebind<_ValPtr, _Node_base>;
+      using _Header_t = __rb_tree::_Header<_Node_base>;
+      using _Iterator = __rb_tree::_Iterator<false, _ValPtr>;
+      using _Const_iterator = __rb_tree::_Iterator<true, _ValPtr>;
+
+      static void
+      _Rotate_left(_Base_ptr __x, _Base_ptr& __root)
+      {
+	const _Base_ptr __y = __x->_M_right;
+
+	__x->_M_right = __y->_M_left;
+	if (__y->_M_left)
+	  __y->_M_left->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_left)
+	  __x->_M_parent->_M_left = __y;
+	else
+	  __x->_M_parent->_M_right = __y;
+	__y->_M_left = __x;
+	__x->_M_parent = __y;
+      }
+
+      static void
+      _Rotate_right(_Base_ptr __x, _Base_ptr& __root)
+      {
+	const _Base_ptr __y = __x->_M_left;
+
+	__x->_M_left = __y->_M_right;
+	if (__y->_M_right)
+	  __y->_M_right->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_right)
+	  __x->_M_parent->_M_right = __y;
+	else
+	  __x->_M_parent->_M_left = __y;
+	__y->_M_right = __x;
+	__x->_M_parent = __y;
+      }
+
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _Base_ptr __x, _Base_ptr __p,
+			      _Node_base& __header)
+      {
+	_Base_ptr& __root = __header._M_parent;
+
+	// Initialize fields in new node to insert.
+	__x->_M_parent = __p;
+	__x->_M_left = __x->_M_right = nullptr;
+	__x->_M_color = _S_red;
+
+	// Insert.
+	// Make new node child of parent and maintain root, leftmost and
+	// rightmost nodes.
+	// N.B. First node is always inserted left.
+	if (__insert_left)
+	  {
+	    __p->_M_left = __x; // also makes leftmost = __x when __p == &__header
+
+	    if (std::__to_address(__p) == std::addressof(__header))
+	      {
+		__header._M_parent = __x;
+		__header._M_right = __x;
+	      }
+	    else if (__p == __header._M_left)
+	      __header._M_left = __x; // maintain leftmost pointing to min node
+	  }
+	else
+	  {
+	    __p->_M_right = __x;
+
+	    if (__p == __header._M_right)
+	      __header._M_right = __x; // maintain rightmost pointing to max node
+	  }
+	// Rebalance.
+	while (__x != __root
+	       && __x->_M_parent->_M_color == _S_red)
+	  {
+	    const _Base_ptr __xpp = __x->_M_parent->_M_parent;
+
+	    if (__x->_M_parent == __xpp->_M_left)
+	      {
+		const _Base_ptr __y = __xpp->_M_right;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_right)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_left(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_right(__xpp, __root);
+		  }
+	      }
+	    else
+	      {
+		const _Base_ptr __y = __xpp->_M_left;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_left)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_right(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_left(__xpp, __root);
+		  }
+	      }
+	  }
+	__root->_M_color = _S_black;
+      }
+
+      static _Base_ptr
+      _S_rebalance_for_erase(_Base_ptr __z, _Node_base& __header)
+      {
+	_Base_ptr& __root = __header._M_parent;
+	_Base_ptr& __leftmost = __header._M_left;
+	_Base_ptr& __rightmost = __header._M_right;
+	_Base_ptr __y = __z;
+	_Base_ptr __x{};
+	_Base_ptr __x_parent{};
+
+	if (!__y->_M_left)     // __z has at most one non-null child. y == z.
+	  __x = __y->_M_right;     // __x might be null.
+	else
+	  if (!__y->_M_right)  // __z has exactly one non-null child. y == z.
+	    __x = __y->_M_left;    // __x is not null.
+	  else
+	    {
+	      // __z has two non-null children.  Set __y to
+	      __y = __y->_M_right;   //   __z's successor.  __x might be null.
+	      while (__y->_M_left)
+		__y = __y->_M_left;
+	      __x = __y->_M_right;
+	    }
+	if (__y != __z)
+	  {
+	    // relink y in place of z.  y is z's successor
+	    __z->_M_left->_M_parent = __y;
+	    __y->_M_left = __z->_M_left;
+	    if (__y != __z->_M_right)
+	      {
+		__x_parent = __y->_M_parent;
+		if (__x)
+		  __x->_M_parent = __y->_M_parent;
+		__y->_M_parent->_M_left = __x;   // __y must be a child of _M_left
+		__y->_M_right = __z->_M_right;
+		__z->_M_right->_M_parent = __y;
+	      }
+	    else
+	      __x_parent = __y;
+	    if (__root == __z)
+	      __root = __y;
+	    else if (__z->_M_parent->_M_left == __z)
+	      __z->_M_parent->_M_left = __y;
+	    else
+	      __z->_M_parent->_M_right = __y;
+	    __y->_M_parent = __z->_M_parent;
+	    std::swap(__y->_M_color, __z->_M_color);
+	    __y = __z;
+	    // __y now points to node to be actually deleted
+	  }
+	else
+	  {                        // __y == __z
+	    __x_parent = __y->_M_parent;
+	    if (__x)
+	      __x->_M_parent = __y->_M_parent;
+	    if (__root == __z)
+	      __root = __x;
+	    else
+	      if (__z->_M_parent->_M_left == __z)
+		__z->_M_parent->_M_left = __x;
+	      else
+		__z->_M_parent->_M_right = __x;
+	    if (__leftmost == __z)
+	      {
+		if (!__z->_M_right)        // __z->_M_left must be null also
+		  __leftmost = __z->_M_parent;
+		// makes __leftmost == _M_header if __z == __root
+		else
+		  __leftmost = _Node_base::_S_minimum(__x);
+	      }
+	    if (__rightmost == __z)
+	      {
+		if (__z->_M_left == 0)         // __z->_M_right must be null also
+		  __rightmost = __z->_M_parent;
+		// makes __rightmost == _M_header if __z == __root
+		else                      // __x == __z->_M_left
+		  __rightmost = _Node_base::_S_maximum(__x);
+	      }
+	  }
+	if (__y->_M_color != _S_red)
+	  {
+	    while (__x != __root && (__x == 0 || __x->_M_color == _S_black))
+	      if (__x == __x_parent->_M_left)
+		{
+		  _Base_ptr __w = __x_parent->_M_right;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_left(__x_parent, __root);
+		      __w = __x_parent->_M_right;
+		    }
+		  if ((!__w->_M_left || __w->_M_left->_M_color == _S_black) &&
+		      (!__w->_M_right || __w->_M_right->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_right || __w->_M_right->_M_color == _S_black)
+			{
+			  __w->_M_left->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_right(__w, __root);
+			  __w = __x_parent->_M_right;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_right)
+			__w->_M_right->_M_color = _S_black;
+		      _Rotate_left(__x_parent, __root);
+		      break;
+		    }
+		}
+	      else
+		{
+		  // same as above, with _M_right <-> _M_left.
+		  _Base_ptr __w = __x_parent->_M_left;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_right(__x_parent, __root);
+		      __w = __x_parent->_M_left;
+		    }
+		  if ((!__w->_M_right || __w->_M_right->_M_color == _S_black) &&
+		      (!__w->_M_left || __w->_M_left->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_left || __w->_M_left->_M_color == _S_black)
+			{
+			  __w->_M_right->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_left(__w, __root);
+			  __w = __x_parent->_M_left;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_left)
+			__w->_M_left->_M_color = _S_black;
+		      _Rotate_right(__x_parent, __root);
+		      break;
+		    }
+		}
+	    if (__x)
+	      __x->_M_color = _S_black;
+	  }
+
+	return __y;
+      }
+    };
+#endif
+} // namespace __rb_tree
+
 #if __cplusplus > 201402L
   template<typename _Tree1, typename _Cmp2>
     struct _Rb_tree_merge_helper { };
@@ -425,15 +1020,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     class _Rb_tree
     {
       typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
-	rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
+	rebind<_Val>::other _Val_alloc_type;
+
+      typedef __gnu_cxx::__alloc_traits<_Val_alloc_type> _Val_alloc_traits;
+      typedef typename _Val_alloc_traits::pointer _ValPtr;
+      typedef __rb_tree::_Node_traits<_Val, _ValPtr> _Node_traits;
+
+      typedef typename _Node_traits::_Node_base		_Node_base;
+      typedef typename _Node_traits::_Node		_Node;
+
+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
+	rebind<_Node>::other _Node_allocator;
 
-      typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
+      typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Node_alloc_traits;
 
     protected:
-      typedef _Rb_tree_node_base* 		_Base_ptr;
-      typedef const _Rb_tree_node_base* 	_Const_Base_ptr;
-      typedef _Rb_tree_node<_Val>* 		_Link_type;
-      typedef const _Rb_tree_node<_Val>*	_Const_Link_type;
+      typedef typename _Node_traits::_Base_ptr	_Base_ptr;
+      typedef typename _Node_traits::_Node_ptr	_Node_ptr;
 
     private:
       // Functor recycling a pool of nodes and using allocation once the pool
@@ -445,13 +1048,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  if (_M_root)
 	    {
-	      _M_root->_M_parent = 0;
+	      _M_root->_M_parent = _Base_ptr();
 
 	      if (_M_nodes->_M_left)
 		_M_nodes = _M_nodes->_M_left;
 	    }
 	  else
-	    _M_nodes = 0;
+	    _M_nodes = _Base_ptr();
 	}
 
 #if __cplusplus >= 201103L
@@ -459,15 +1062,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
 	~_Reuse_or_alloc_node()
-	{ _M_t._M_erase(static_cast<_Link_type>(_M_root)); }
+	{
+	  if (_M_root)
+	    _M_t._M_erase(static_cast<_Node&>(*_M_root)._M_node_ptr());
+	}
 
 	template<typename _Arg>
-	  _Link_type
+	  _Node_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg)
 	  {
-	    _Link_type __node = static_cast<_Link_type>(_M_extract());
-	    if (__node)
+	    _Base_ptr __base = _M_extract();
+	    if (__base)
 	      {
+		_Node_ptr __node = static_cast<_Node&>(*__base)._M_node_ptr();
 		_M_t._M_destroy_node(__node);
 		_M_t._M_construct_node(__node, _GLIBCXX_FORWARD(_Arg, __arg));
 		return __node;
@@ -489,7 +1096,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    {
 	      if (_M_nodes->_M_right == __node)
 		{
-		  _M_nodes->_M_right = 0;
+		  _M_nodes->_M_right = _Base_ptr();
 
 		  if (_M_nodes->_M_left)
 		    {
@@ -503,10 +1110,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		    }
 		}
 	      else // __node is on the left.
-		_M_nodes->_M_left = 0;
+		_M_nodes->_M_left = _Base_ptr();
 	    }
 	  else
-	    _M_root = 0;
+	    _M_root = _Base_ptr();
 
 	  return __node;
 	}
@@ -524,7 +1131,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: _M_t(__t) { }
 
 	template<typename _Arg>
-	  _Link_type
+	  _Node_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg) const
 	  { return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg)); }
 
@@ -556,17 +1163,52 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return allocator_type(_M_get_Node_allocator()); }
 
     protected:
-      _Link_type
+      _Node_ptr
       _M_get_node()
-      { return _Alloc_traits::allocate(_M_get_Node_allocator(), 1); }
+      {
+#if __cplusplus < 201102L || _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+	return _Node_alloc_traits::allocate(_M_get_Node_allocator(), 1);
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
+	using __alloc_pointer = typename _Node_alloc_traits::pointer;
+	if constexpr (is_same<_Node_ptr, __alloc_pointer>::value)
+	  return _Node_alloc_traits::allocate(_M_get_Node_allocator(), 1);
+	else
+	  {
+	    auto __ptr =
+	      _Node_alloc_traits::allocate(_M_get_Node_allocator(), 1);
+	    return std::__to_address(__ptr);
+	  }
+#pragma GCC diagnostic pop
+#endif
+      }
 
       void
-      _M_put_node(_Link_type __p) _GLIBCXX_NOEXCEPT
-      { _Alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1); }
+      _M_put_node(_Node_ptr __p) _GLIBCXX_NOEXCEPT
+      {
+#if __cplusplus < 201102L || _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+	_Node_alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1);
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
+	using __alloc_pointer = typename _Node_alloc_traits::pointer;
+	if constexpr (is_same<_Node_ptr, __alloc_pointer>::value)
+	  _Node_alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1);
+	else
+	  {
+	    // When not using the allocator's pointer type internally we must
+	    // convert __p to __alloc_pointer so it can be deallocated.
+	    auto __ap = pointer_traits<__alloc_pointer>::pointer_to(*__p);
+	    _Node_alloc_traits::deallocate(_M_get_Node_allocator(), __ap, 1);
+	  }
+#pragma GCC diagnostic pop
+#endif
+      }
 
 #if __cplusplus < 201103L
       void
-      _M_construct_node(_Link_type __node, const value_type& __x)
+      _M_construct_node(_Node_ptr __node, const value_type& __x)
       {
 	__try
 	  { get_allocator().construct(__node->_M_valptr(), __x); }
@@ -577,79 +1219,80 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  }
       }
 
-      _Link_type
+      _Node_ptr
       _M_create_node(const value_type& __x)
       {
-	_Link_type __tmp = _M_get_node();
+	_Node_ptr __tmp = _M_get_node();
 	_M_construct_node(__tmp, __x);
 	return __tmp;
       }
 #else
       template<typename... _Args>
 	void
-	_M_construct_node(_Link_type __node, _Args&&... __args)
+	_M_construct_node(_Node_ptr __node, _Args&&... __args)
 	{
 	  __try
 	    {
-	      ::new(__node) _Rb_tree_node<_Val>;
-	      _Alloc_traits::construct(_M_get_Node_allocator(),
-				       __node->_M_valptr(),
-				       std::forward<_Args>(__args)...);
+	      ::new(std::addressof(*__node)) _Node;
+	      _Node_alloc_traits::construct(_M_get_Node_allocator(),
+					    __node->_M_valptr(),
+					    std::forward<_Args>(__args)...);
 	    }
 	  __catch(...)
 	    {
-	      __node->~_Rb_tree_node<_Val>();
+	      __node->~_Node();
 	      _M_put_node(__node);
 	      __throw_exception_again;
 	    }
 	}
 
       template<typename... _Args>
-	_Link_type
+	_Node_ptr
 	_M_create_node(_Args&&... __args)
 	{
-	  _Link_type __tmp = _M_get_node();
+	  _Node_ptr __tmp = _M_get_node();
 	  _M_construct_node(__tmp, std::forward<_Args>(__args)...);
 	  return __tmp;
 	}
 #endif
 
       void
-      _M_destroy_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_destroy_node(_Node_ptr __p) _GLIBCXX_NOEXCEPT
       {
 #if __cplusplus < 201103L
 	get_allocator().destroy(__p->_M_valptr());
 #else
-	_Alloc_traits::destroy(_M_get_Node_allocator(), __p->_M_valptr());
-	__p->~_Rb_tree_node<_Val>();
+	_Node_alloc_traits::destroy(_M_get_Node_allocator(), __p->_M_valptr());
+	__p->~_Node();
 #endif
       }
 
       void
-      _M_drop_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_drop_node(_Node_ptr __p) _GLIBCXX_NOEXCEPT
       {
 	_M_destroy_node(__p);
 	_M_put_node(__p);
       }
 
       template<bool _MoveValue, typename _NodeGen>
-	_Link_type
-	_M_clone_node(_Link_type __x, _NodeGen& __node_gen)
+	_Node_ptr
+	_M_clone_node(_Node_ptr __x, _NodeGen& __node_gen)
 	{
 #if __cplusplus >= 201103L
 	  using _Vp = __conditional_t<_MoveValue,
 				      value_type&&,
 				      const value_type&>;
 #endif
-	  _Link_type __tmp
+	  _Node_ptr __tmp
 	    = __node_gen(_GLIBCXX_FORWARD(_Vp, *__x->_M_valptr()));
 	  __tmp->_M_color = __x->_M_color;
-	  __tmp->_M_left = 0;
-	  __tmp->_M_right = 0;
+	  __tmp->_M_left = __tmp->_M_right = _Base_ptr();
 	  return __tmp;
 	}
 
     protected:
+      typedef typename _Node_traits::_Header_t		_Header_t;
+
 #if _GLIBCXX_INLINE_VERSION
       template<typename _Key_compare>
 #else
@@ -660,7 +1303,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	struct _Rb_tree_impl
 	: public _Node_allocator
 	, public _Rb_tree_key_compare<_Key_compare>
-	, public _Rb_tree_header
+	, public _Header_t
 	{
 	  typedef _Rb_tree_key_compare<_Key_compare> _Base_key_compare;
 
@@ -672,9 +1315,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  { }
 
 	  _Rb_tree_impl(const _Rb_tree_impl& __x)
-	  : _Node_allocator(_Alloc_traits::_S_select_on_copy(__x))
+	  : _Node_allocator(_Node_alloc_traits::_S_select_on_copy(__x))
 	  , _Base_key_compare(__x._M_key_compare)
-	  , _Rb_tree_header()
+	  , _Header_t()
 	  { }
 
 #if __cplusplus < 201103L
@@ -694,7 +1337,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Rb_tree_impl(_Rb_tree_impl&& __x, _Node_allocator&& __a)
 	  : _Node_allocator(std::move(__a)),
 	    _Base_key_compare(std::move(__x)),
-	    _Rb_tree_header(std::move(__x))
+	    _Header_t(std::move(__x))
 	  { }
 
 	  _Rb_tree_impl(const _Key_compare& __comp, _Node_allocator&& __a)
@@ -710,7 +1353,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_root() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_parent; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_root() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_parent; }
 
@@ -718,7 +1361,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_leftmost() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_left; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_leftmost() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_left; }
 
@@ -726,35 +1369,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_rightmost() _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_right; }
 
-      _Const_Base_ptr
+      _Base_ptr
       _M_rightmost() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_right; }
 
-      _Link_type
-      _M_mbegin() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(this->_M_impl._M_header._M_parent); }
-
-      _Link_type
-      _M_begin() _GLIBCXX_NOEXCEPT
-      { return _M_mbegin(); }
-
-      _Const_Link_type
+      _Base_ptr
       _M_begin() const _GLIBCXX_NOEXCEPT
+      { return this->_M_impl._M_header._M_parent; }
+
+      _Node_ptr
+      _M_begin_node() const _GLIBCXX_NOEXCEPT
       {
-	return static_cast<_Const_Link_type>
-	  (this->_M_impl._M_header._M_parent);
+	_Base_ptr __begin = this->_M_impl._M_header._M_parent;
+	return __begin
+	  ? static_cast<_Node&>(*__begin)._M_node_ptr()
+	  : _Node_ptr();
       }
 
       _Base_ptr
-      _M_end() _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
-
-      _Const_Base_ptr
       _M_end() const _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
+      { return this->_M_impl._M_header._M_base_ptr(); }
 
       static const _Key&
-      _S_key(_Const_Link_type __x)
+      _S_key(const _Node& __node)
       {
 #if __cplusplus >= 201103L
 	// If we're asking for the key we're presumably using the comparison
@@ -772,48 +1409,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 # endif // C++17
 #endif // C++11
 
-	return _KeyOfValue()(*__x->_M_valptr());
+	return _KeyOfValue()(*__node._M_valptr());
       }
 
-      static _Link_type
-      _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_left); }
-
-      static _Const_Link_type
-      _S_left(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_left); }
-
-      static _Link_type
-      _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_right); }
-
-      static _Const_Link_type
-      _S_right(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_right); }
+      static const _Key&
+      _S_key(_Base_ptr __x)
+      { return _S_key(static_cast<const _Node&>(*__x)); }
 
       static const _Key&
-      _S_key(_Const_Base_ptr __x)
-      { return _S_key(static_cast<_Const_Link_type>(__x)); }
+      _S_key(_Node_ptr __x)
+      { return _S_key(*__x); }
 
       static _Base_ptr
-      _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
+      _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      { return __x->_M_left; }
 
-      static _Const_Base_ptr
-      _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
+      static _Node_ptr
+      _S_left(_Node_ptr __x)
+      {
+	return __x->_M_left
+	  ? static_cast<_Node&>(*__x->_M_left)._M_node_ptr()
+	  : _Node_ptr();
+      }
 
       static _Base_ptr
-      _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
+      _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      { return __x->_M_right; }
 
-      static _Const_Base_ptr
-      _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
+      static _Node_ptr
+      _S_right(_Node_ptr __x) _GLIBCXX_NOEXCEPT
+      {
+	return __x->_M_right
+	  ? static_cast<_Node&>(*__x->_M_right)._M_node_ptr()
+	  : _Node_ptr();
+      }
 
     public:
-      typedef _Rb_tree_iterator<value_type>       iterator;
-      typedef _Rb_tree_const_iterator<value_type> const_iterator;
+      typedef typename _Node_traits::_Iterator		iterator;
+      typedef typename _Node_traits::_Const_iterator	const_iterator;
 
       typedef std::reverse_iterator<iterator>       reverse_iterator;
       typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
@@ -846,7 +1479,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_(_Base_ptr __x, _Base_ptr __y, _Arg&& __v, _NodeGen&);
 
       iterator
-      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Link_type __z);
+      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Node_ptr __z);
 
       template<typename _Arg>
 	iterator
@@ -857,10 +1490,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_equal_lower(_Arg&& __x);
 
       iterator
-      _M_insert_lower_node(_Base_ptr __p, _Link_type __z);
+      _M_insert_lower_node(_Base_ptr __p, _Node_ptr __z);
 
       iterator
-      _M_insert_equal_lower_node(_Link_type __z);
+      _M_insert_equal_lower_node(_Node_ptr __z);
 #else
       template<typename _NodeGen>
 	iterator
@@ -879,22 +1512,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       enum { __as_lvalue, __as_rvalue };
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
-	_M_copy(_Link_type, _Base_ptr, _NodeGen&);
+	_Base_ptr
+	_M_copy(_Node_ptr, _Base_ptr, _NodeGen&);
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
+	_Base_ptr
 	_M_copy(const _Rb_tree& __x, _NodeGen& __gen)
 	{
-	  _Link_type __root =
-	    _M_copy<_MoveValues>(__x._M_mbegin(), _M_end(), __gen);
-	  _M_leftmost() = _S_minimum(__root);
-	  _M_rightmost() = _S_maximum(__root);
+	  _Base_ptr __root =
+	    _M_copy<_MoveValues>(__x._M_begin_node(), _M_end(), __gen);
+	  _M_leftmost() = _Node_base::_S_minimum(__root);
+	  _M_rightmost() = _Node_base::_S_maximum(__root);
 	  _M_impl._M_node_count = __x._M_impl._M_node_count;
 	  return __root;
 	}
 
-      _Link_type
+      _Base_ptr
       _M_copy(const _Rb_tree& __x)
       {
 	_Alloc_node __an(*this);
@@ -902,22 +1535,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       void
-      _M_erase(_Link_type __x);
-
-      iterator
-      _M_lower_bound(_Link_type __x, _Base_ptr __y,
-		     const _Key& __k);
+      _M_erase(_Node_ptr __x);
 
-      const_iterator
-      _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _Base_ptr
+      _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k) const;
 
-      iterator
-      _M_upper_bound(_Link_type __x, _Base_ptr __y,
-		     const _Key& __k);
-
-      const_iterator
-      _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _Base_ptr
+      _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k) const;
 
     public:
@@ -935,7 +1560,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x)
       : _M_impl(__x._M_impl)
       {
-	if (__x._M_root() != 0)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -947,7 +1572,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x, const allocator_type& __a)
       : _M_impl(__x._M_impl._M_key_compare, _Node_allocator(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -966,7 +1591,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a, false_type)
       : _M_impl(__x._M_impl._M_key_compare, std::move(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_move_data(__x, false_type{});
       }
 
@@ -974,14 +1599,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a)
       noexcept( noexcept(
 	_Rb_tree(std::declval<_Rb_tree&&>(), std::declval<_Node_allocator&&>(),
-		 std::declval<typename _Alloc_traits::is_always_equal>())) )
+		 std::declval<typename _Node_alloc_traits::is_always_equal>())) )
       : _Rb_tree(std::move(__x), std::move(__a),
-		 typename _Alloc_traits::is_always_equal{})
+		 typename _Node_alloc_traits::is_always_equal{})
       { }
 #endif
 
       ~_Rb_tree() _GLIBCXX_NOEXCEPT
-      { _M_erase(_M_begin()); }
+      { _M_erase(_M_begin_node()); }
 
       _Rb_tree&
       operator=(const _Rb_tree& __x);
@@ -1001,11 +1626,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       end() _GLIBCXX_NOEXCEPT
-      { return iterator(&this->_M_impl._M_header); }
+      { return iterator(_M_end()); }
 
       const_iterator
       end() const _GLIBCXX_NOEXCEPT
-      { return const_iterator(&this->_M_impl._M_header); }
+      { return const_iterator(_M_end()); }
 
       reverse_iterator
       rbegin() _GLIBCXX_NOEXCEPT
@@ -1033,7 +1658,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       size_type
       max_size() const _GLIBCXX_NOEXCEPT
-      { return _Alloc_traits::max_size(_M_get_Node_allocator()); }
+      { return _Node_alloc_traits::max_size(_M_get_Node_allocator()); }
 
       void
       swap(_Rb_tree& __t)
@@ -1194,7 +1819,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	const_iterator __result = __position;
 	++__result;
 	_M_erase_aux(__position);
-	return __result._M_const_cast();
+	return iterator(__result._M_node);
       }
 
       // LWG 2059.
@@ -1235,7 +1860,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       erase(const_iterator __first, const_iterator __last)
       {
 	_M_erase_aux(__first, __last);
-	return __last._M_const_cast();
+	return iterator(__last._M_node);
       }
 #else
       void
@@ -1250,7 +1875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       void
       clear() _GLIBCXX_NOEXCEPT
       {
-	_M_erase(_M_begin());
+	_M_erase(_M_begin_node());
 	_M_impl._M_reset();
       }
 
@@ -1266,19 +1891,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       lower_bound(const key_type& __k)
-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
+      { return iterator(_M_lower_bound(_M_begin(), _M_end(), __k)); }
 
       const_iterator
       lower_bound(const key_type& __k) const
-      { return _M_lower_bound(_M_begin(), _M_end(), __k); }
+      {
+	return const_iterator
+	  (_M_lower_bound(_M_begin(), _M_end(), __k));
+      }
 
       iterator
       upper_bound(const key_type& __k)
-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
+      { return iterator(_M_upper_bound(_M_begin(), _M_end(), __k)); }
 
       const_iterator
       upper_bound(const key_type& __k) const
-      { return _M_upper_bound(_M_begin(), _M_end(), __k); }
+      {
+	return const_iterator
+	  (_M_upper_bound(_M_begin(), _M_end(), __k));
+      }
 
       pair<iterator, iterator>
       equal_range(const key_type& __k);
@@ -1293,7 +1924,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_find_tr(const _Kt& __k)
 	{
 	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_find_tr(__k)._M_const_cast();
+	  return iterator(__const_this->_M_find_tr(__k)._M_node);
 	}
 
       template<typename _Kt,
@@ -1301,7 +1932,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	const_iterator
 	_M_find_tr(const _Kt& __k) const
 	{
-	  auto __j = _M_lower_bound_tr(__k);
+	  const_iterator __j(_M_lower_bound_tr(__k));
 	  if (__j != end() && _M_impl._M_key_compare(__k, _S_key(__j._M_node)))
 	    __j = end();
 	  return __j;
@@ -1318,21 +1949,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename _Kt,
 	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	iterator
-	_M_lower_bound_tr(const _Kt& __k)
-	{
-	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_lower_bound_tr(__k)._M_const_cast();
-	}
-
-      template<typename _Kt,
-	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	const_iterator
+	_Base_ptr
 	_M_lower_bound_tr(const _Kt& __k) const
 	{
 	  auto __x = _M_begin();
 	  auto __y = _M_end();
-	  while (__x != 0)
+	  while (__x)
 	    if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	      {
 		__y = __x;
@@ -1340,26 +1962,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      }
 	    else
 	      __x = _S_right(__x);
-	  return const_iterator(__y);
-	}
-
-      template<typename _Kt,
-	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	iterator
-	_M_upper_bound_tr(const _Kt& __k)
-	{
-	  const _Rb_tree* __const_this = this;
-	  return __const_this->_M_upper_bound_tr(__k)._M_const_cast();
+	  return __y;
 	}
 
       template<typename _Kt,
 	       typename _Req = __has_is_transparent_t<_Compare, _Kt>>
-	const_iterator
+	_Base_ptr
 	_M_upper_bound_tr(const _Kt& __k) const
 	{
 	  auto __x = _M_begin();
 	  auto __y = _M_end();
-	  while (__x != 0)
+	  while (__x)
 	    if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	      {
 		__y = __x;
@@ -1367,7 +1980,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      }
 	    else
 	      __x = _S_right(__x);
-	  return const_iterator(__y);
+	  return __y;
 	}
 
       template<typename _Kt,
@@ -1377,7 +1990,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  const _Rb_tree* __const_this = this;
 	  auto __ret = __const_this->_M_equal_range_tr(__k);
-	  return { __ret.first._M_const_cast(), __ret.second._M_const_cast() };
+	  return
+	    { iterator(__ret.first._M_node), iterator(__ret.second._M_node) };
 	}
 
       template<typename _Kt,
@@ -1385,7 +1999,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	pair<const_iterator, const_iterator>
 	_M_equal_range_tr(const _Kt& __k) const
 	{
-	  auto __low = _M_lower_bound_tr(__k);
+	  const_iterator __low(_M_lower_bound_tr(__k));
 	  auto __high = __low;
 	  auto& __cmp = _M_impl._M_key_compare;
 	  while (__high != end() && !__cmp(__k, _S_key(__high._M_node)))
@@ -1401,7 +2015,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #if __cplusplus >= 201103L
       _Rb_tree&
       operator=(_Rb_tree&&)
-      noexcept(_Alloc_traits::_S_nothrow_move()
+      noexcept(_Node_alloc_traits::_S_nothrow_move()
 	       && is_nothrow_move_assignable<_Compare>::value);
 
       template<typename _Iterator>
@@ -1434,6 +2048,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
 #if __glibcxx_node_extract // >= C++17
+      static _Node_ptr
+      _S_adapt(typename _Node_alloc_traits::pointer __ptr)
+      {
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+	return __ptr;
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
+	using __alloc_ptr = typename _Node_alloc_traits::pointer;
+	if constexpr (is_same<_Node_ptr, __alloc_ptr>::value)
+	  return __ptr;
+	else
+	  return std::__to_address(__ptr);
+#pragma GCC diagnostic pop
+#endif
+      }
+
     public:
       /// Re-insert an extracted node.
       insert_return_type
@@ -1450,7 +2081,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    if (__res.second)
 	      {
 		__ret.position
-		  = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+		  = _M_insert_node(__res.first, __res.second,
+				   _S_adapt(__nh._M_ptr));
 		__nh.release();
 		__ret.inserted = true;
 	      }
@@ -1476,9 +2108,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
 	    auto __res = _M_get_insert_equal_pos(__nh._M_key());
 	    if (__res.second)
-	      __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+	      __ret = _M_insert_node(__res.first, __res.second,
+				     _S_adapt(__nh._M_ptr));
 	    else
-	      __ret = _M_insert_equal_lower_node(__nh._M_ptr);
+	      __ret = _M_insert_equal_lower_node(_S_adapt(__nh._M_ptr));
 	    __nh.release();
 	  }
 	return __ret;
@@ -1497,7 +2130,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    auto __res = _M_get_insert_hint_unique_pos(__hint, __nh._M_key());
 	    if (__res.second)
 	      {
-		__ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+		__ret = _M_insert_node(__res.first, __res.second,
+				       _S_adapt(__nh._M_ptr));
 		__nh.release();
 	      }
 	    else
@@ -1518,9 +2152,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
 	    auto __res = _M_get_insert_hint_equal_pos(__hint, __nh._M_key());
 	    if (__res.second)
-	      __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+	      __ret = _M_insert_node(__res.first, __res.second,
+				     _S_adapt(__nh._M_ptr));
 	    else
-	      __ret = _M_insert_equal_lower_node(__nh._M_ptr);
+	      __ret = _M_insert_equal_lower_node(_S_adapt(__nh._M_ptr));
 	    __nh.release();
 	  }
 	return __ret;
@@ -1530,10 +2165,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       node_type
       extract(const_iterator __pos)
       {
-	auto __ptr = _Rb_tree_rebalance_for_erase(
-	    __pos._M_const_cast()._M_node, _M_impl._M_header);
+	auto __ptr = _Node_traits::_S_rebalance_for_erase
+	  (__pos._M_node, _M_impl._M_header);
 	--_M_impl._M_node_count;
-	return { static_cast<_Link_type>(__ptr), _M_get_Node_allocator() };
+	auto __node_ptr = static_cast<_Node&>(*__ptr)._M_node_ptr();
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE
+	return { __node_ptr, _M_get_Node_allocator() };
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
+	using __alloc_ptr = typename _Node_alloc_traits::pointer;
+	if constexpr (is_same<_Node_ptr, __alloc_ptr>::value)
+	  return { __node_ptr, _M_get_Node_allocator() };
+	else
+	  {
+	    auto __ap = pointer_traits<__alloc_ptr>::pointer_to(*__node_ptr);
+	    return { __ap, _M_get_Node_allocator() };
+	  }
+#pragma GCC diagnostic pop
+#endif
       }
 
       /// Extract a node.
@@ -1567,11 +2217,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr = _Node_traits::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
-		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+		  auto __node_ptr = static_cast<_Node&>(*__ptr)._M_node_ptr();
+		  _M_insert_node(__res.first, __res.second, __node_ptr);
 		}
 	    }
 	}
@@ -1589,11 +2239,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr = _Node_traits::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
-		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+		  auto __node_ptr = static_cast<_Node&>(*__ptr)._M_node_ptr();
+		  _M_insert_node(__res.first, __res.second, __node_ptr);
 		}
 	    }
 	}
@@ -1666,7 +2316,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
 	_Rb_tree& _M_t;
-	_Link_type _M_node;
+	_Node_ptr _M_node;
       };
 #endif // C++11
     };
@@ -1704,7 +2354,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_move_assign(_Rb_tree& __x, true_type)
     {
       clear();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	_M_move_data(__x, true_type());
       std::__alloc_on_move(_M_get_Node_allocator(),
 			   __x._M_get_Node_allocator());
@@ -1723,7 +2373,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // structure.
       _Reuse_or_alloc_node __roan(*this);
       _M_impl._M_reset();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	{
 	  _M_root() = _M_copy<__as_rvalue>(__x, __roan);
 	  __x.clear();
@@ -1735,11 +2385,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     operator=(_Rb_tree&& __x)
-    noexcept(_Alloc_traits::_S_nothrow_move()
+    noexcept(_Node_alloc_traits::_S_nothrow_move()
 	     && is_nothrow_move_assignable<_Compare>::value)
     {
       _M_impl._M_key_compare = std::move(__x._M_impl._M_key_compare);
-      _M_move_assign(__x, __bool_constant<_Alloc_traits::_S_nothrow_move()>());
+      _M_move_assign(__x,
+		     __bool_constant<_Node_alloc_traits::_S_nothrow_move()>());
       return *this;
     }
 
@@ -1780,11 +2431,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  // Note that _Key may be a constant type.
 #if __cplusplus >= 201103L
-	  if (_Alloc_traits::_S_propagate_on_copy_assign())
+	  if (_Node_alloc_traits::_S_propagate_on_copy_assign())
 	    {
 	      auto& __this_alloc = this->_M_get_Node_allocator();
 	      auto& __that_alloc = __x._M_get_Node_allocator();
-	      if (!_Alloc_traits::_S_always_equal()
+	      if (!_Node_alloc_traits::_S_always_equal()
 		  && __this_alloc != __that_alloc)
 		{
 		  // Replacement allocator cannot free existing storage, we need
@@ -1798,7 +2449,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Reuse_or_alloc_node __roan(*this);
 	  _M_impl._M_reset();
 	  _M_impl._M_key_compare = __x._M_impl._M_key_compare;
-	  if (__x._M_root() != 0)
+	  if (__x._M_root())
 	    _M_root() = _M_copy<__as_lvalue>(__x, __roan);
 	}
 
@@ -1822,14 +2473,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 		 _NodeGen& __node_gen)
       {
-	bool __insert_left = (__x != 0 || __p == _M_end()
+	bool __insert_left = (__x || __p == _M_end()
 			      || _M_impl._M_key_compare(_KeyOfValue()(__v),
 							_S_key(__p)));
 
-	_Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
+	_Base_ptr __z =
+	  __node_gen(_GLIBCXX_FORWARD(_Arg, __v))->_M_base_ptr();
 
-	_Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				      this->_M_impl._M_header);
+	_Node_traits::_S_insert_and_rebalance
+	  (__insert_left, __z, __p, this->_M_impl._M_header);
 	++_M_impl._M_node_count;
 	return iterator(__z);
       }
@@ -1851,10 +2503,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _KeyOfValue()(__v)));
 
-      _Link_type __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
-
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Base_ptr __z =
+	_M_create_node(_GLIBCXX_FORWARD(_Arg, __v))->_M_base_ptr();
+      _Node_traits::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -1872,9 +2524,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_insert_equal_lower(const _Val& __v)
 #endif
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = !_M_impl._M_key_compare(_S_key(__x), _KeyOfValue()(__v)) ?
@@ -1886,25 +2538,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Key, typename _Val, typename _KoV,
 	   typename _Compare, typename _Alloc>
     template<bool _MoveValues, typename _NodeGen>
-      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Link_type
+      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Base_ptr
       _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::
-      _M_copy(_Link_type __x, _Base_ptr __p, _NodeGen& __node_gen)
+      _M_copy(_Node_ptr __x, _Base_ptr __p, _NodeGen& __node_gen)
       {
 	// Structural copy. __x and __p must be non-null.
-	_Link_type __top = _M_clone_node<_MoveValues>(__x, __node_gen);
+	_Node_ptr __top = _M_clone_node<_MoveValues>(__x, __node_gen);
+	_Base_ptr __top_base = __top->_M_base_ptr();
 	__top->_M_parent = __p;
 
 	__try
 	  {
 	    if (__x->_M_right)
 	      __top->_M_right =
-		_M_copy<_MoveValues>(_S_right(__x), __top, __node_gen);
-	    __p = __top;
+		_M_copy<_MoveValues>(_S_right(__x), __top_base, __node_gen);
+	    __p = __top_base;
 	    __x = _S_left(__x);
 
-	    while (__x != 0)
+	    while (__x)
 	      {
-		_Link_type __y = _M_clone_node<_MoveValues>(__x, __node_gen);
+		_Base_ptr __y =
+		  _M_clone_node<_MoveValues>(__x, __node_gen)->_M_base_ptr();
 		__p->_M_left = __y;
 		__y->_M_parent = __p;
 		if (__x->_M_right)
@@ -1919,20 +2573,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    _M_erase(__top);
 	    __throw_exception_again;
 	  }
-	return __top;
+	return __top_base;
       }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     void
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_erase(_Link_type __x)
+    _M_erase(_Node_ptr __x)
     {
       // Erase without rebalancing.
-      while (__x != 0)
+      while (__x)
 	{
 	  _M_erase(_S_right(__x));
-	  _Link_type __y = _S_left(__x);
+	  _Node_ptr __y = _S_left(__x);
 	  _M_drop_node(__x);
 	  __x = __y;
 	}
@@ -1941,65 +2595,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::iterator
-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Link_type __x, _Base_ptr __y,
-		   const _Key& __k)
-    {
-      while (__x != 0)
-	if (!_M_impl._M_key_compare(_S_key(__x), __k))
-	  __y = __x, __x = _S_left(__x);
-	else
-	  __x = _S_right(__x);
-      return iterator(__y);
-    }
-
-  template<typename _Key, typename _Val, typename _KeyOfValue,
-	   typename _Compare, typename _Alloc>
-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::const_iterator
+		      _Compare, _Alloc>::_Base_ptr
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	  __y = __x, __x = _S_left(__x);
 	else
 	  __x = _S_right(__x);
-      return const_iterator(__y);
+      return __y;
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::iterator
+		      _Compare, _Alloc>::_Base_ptr
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Link_type __x, _Base_ptr __y,
-		   const _Key& __k)
-    {
-      while (__x != 0)
-	if (_M_impl._M_key_compare(__k, _S_key(__x)))
-	  __y = __x, __x = _S_left(__x);
-	else
-	  __x = _S_right(__x);
-      return iterator(__y);
-    }
-
-  template<typename _Key, typename _Val, typename _KeyOfValue,
-	   typename _Compare, typename _Alloc>
-    typename _Rb_tree<_Key, _Val, _KeyOfValue,
-		      _Compare, _Alloc>::const_iterator
-    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	  __y = __x, __x = _S_left(__x);
 	else
 	  __x = _S_right(__x);
-      return const_iterator(__y);
+      return __y;
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2011,9 +2633,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k)
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
 	    __x = _S_right(__x);
@@ -2021,13 +2643,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Link_type __xu(__x);
+	      _Base_ptr __xu(__x);
 	      _Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
-	      return pair<iterator,
-			  iterator>(_M_lower_bound(__x, __y, __k),
-				    _M_upper_bound(__xu, __yu, __k));
+	      return make_pair(iterator(_M_lower_bound(__x, __y, __k)),
+			       iterator(_M_upper_bound(__xu, __yu, __k)));
 	    }
 	}
       return pair<iterator, iterator>(iterator(__y),
@@ -2043,9 +2664,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k) const
     {
-      _Const_Link_type __x = _M_begin();
-      _Const_Base_ptr __y = _M_end();
-      while (__x != 0)
+      _Base_ptr __x = _M_begin();
+      _Base_ptr __y = _M_end();
+      while (__x)
 	{
 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
 	    __x = _S_right(__x);
@@ -2053,13 +2674,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Const_Link_type __xu(__x);
-	      _Const_Base_ptr __yu(__y);
+	      _Base_ptr __xu(__x);
+	      _Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
-	      return pair<const_iterator,
-			  const_iterator>(_M_lower_bound(__x, __y, __k),
-					  _M_upper_bound(__xu, __yu, __k));
+	      return make_pair(const_iterator(_M_lower_bound(__x, __y, __k)),
+			       const_iterator(_M_upper_bound(__xu, __yu, __k)));
 	    }
 	}
       return pair<const_iterator, const_iterator>(const_iterator(__y),
@@ -2073,12 +2693,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     swap(_Rb_tree& __t)
     _GLIBCXX_NOEXCEPT_IF(__is_nothrow_swappable<_Compare>::value)
     {
-      if (_M_root() == 0)
+      if (!_M_root())
 	{
-	  if (__t._M_root() != 0)
+	  if (__t._M_root())
 	    _M_impl._M_move_data(__t._M_impl);
 	}
-      else if (__t._M_root() == 0)
+      else if (!__t._M_root())
 	__t._M_impl._M_move_data(_M_impl);
       else
 	{
@@ -2093,8 +2713,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // No need to swap header's color as it does not change.
       std::swap(this->_M_impl._M_key_compare, __t._M_impl._M_key_compare);
 
-      _Alloc_traits::_S_on_swap(_M_get_Node_allocator(),
-				__t._M_get_Node_allocator());
+      _Node_alloc_traits::_S_on_swap(_M_get_Node_allocator(),
+				     __t._M_get_Node_allocator());
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2107,10 +2727,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_unique_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       bool __comp = true;
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __comp = _M_impl._M_key_compare(__k, _S_key(__x));
@@ -2126,7 +2746,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
       if (_M_impl._M_key_compare(_S_key(__j._M_node), __k))
 	return _Res(__x, __y);
-      return _Res(__j._M_node, 0);
+      return _Res(__j._M_node, _Base_ptr());
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2139,9 +2759,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_equal_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = _M_impl._M_key_compare(__k, _S_key(__x)) ?
@@ -2209,44 +2829,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_hint_unique_pos(const_iterator __position,
 				  const key_type& __k)
     {
-      iterator __pos = __position._M_const_cast();
       typedef pair<_Base_ptr, _Base_ptr> _Res;
 
       // end()
-      if (__pos._M_node == _M_end())
+      if (__position._M_node == _M_end())
 	{
 	  if (size() > 0
 	      && _M_impl._M_key_compare(_S_key(_M_rightmost()), __k))
-	    return _Res(0, _M_rightmost());
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else
 	    return _M_get_insert_unique_pos(__k);
 	}
-      else if (_M_impl._M_key_compare(__k, _S_key(__pos._M_node)))
+      else if (_M_impl._M_key_compare(__k, _S_key(__position._M_node)))
 	{
 	  // First, try before...
-	  iterator __before = __pos;
-	  if (__pos._M_node == _M_leftmost()) // begin()
+	  iterator __before(__position._M_node);
+	  if (__position._M_node == _M_leftmost()) // begin()
 	    return _Res(_M_leftmost(), _M_leftmost());
 	  else if (_M_impl._M_key_compare(_S_key((--__before)._M_node), __k))
 	    {
-	      if (_S_right(__before._M_node) == 0)
-		return _Res(0, __before._M_node);
+	      if (!_S_right(__before._M_node))
+		return _Res(_Base_ptr(), __before._M_node);
 	      else
-		return _Res(__pos._M_node, __pos._M_node);
+		return _Res(__position._M_node, __position._M_node);
 	    }
 	  else
 	    return _M_get_insert_unique_pos(__k);
 	}
-      else if (_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
+      else if (_M_impl._M_key_compare(_S_key(__position._M_node), __k))
 	{
 	  // ... then try after.
-	  iterator __after = __pos;
-	  if (__pos._M_node == _M_rightmost())
-	    return _Res(0, _M_rightmost());
+	  iterator __after(__position._M_node);
+	  if (__position._M_node == _M_rightmost())
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else if (_M_impl._M_key_compare(__k, _S_key((++__after)._M_node)))
 	    {
-	      if (_S_right(__pos._M_node) == 0)
-		return _Res(0, __pos._M_node);
+	      if (!_S_right(__position._M_node))
+		return _Res(_Base_ptr(), __position._M_node);
 	      else
 		return _Res(__after._M_node, __after._M_node);
 	    }
@@ -2255,7 +2874,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
       else
 	// Equivalent keys.
-	return _Res(__pos._M_node, 0);
+	return _Res(__position._M_node, _Base_ptr());
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2294,30 +2913,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     _M_get_insert_hint_equal_pos(const_iterator __position, const key_type& __k)
     {
-      iterator __pos = __position._M_const_cast();
       typedef pair<_Base_ptr, _Base_ptr> _Res;
 
       // end()
-      if (__pos._M_node == _M_end())
+      if (__position._M_node == _M_end())
 	{
 	  if (size() > 0
 	      && !_M_impl._M_key_compare(__k, _S_key(_M_rightmost())))
-	    return _Res(0, _M_rightmost());
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else
 	    return _M_get_insert_equal_pos(__k);
 	}
-      else if (!_M_impl._M_key_compare(_S_key(__pos._M_node), __k))
+      else if (!_M_impl._M_key_compare(_S_key(__position._M_node), __k))
 	{
 	  // First, try before...
-	  iterator __before = __pos;
-	  if (__pos._M_node == _M_leftmost()) // begin()
+	  iterator __before(__position._M_node);
+	  if (__position._M_node == _M_leftmost()) // begin()
 	    return _Res(_M_leftmost(), _M_leftmost());
 	  else if (!_M_impl._M_key_compare(__k, _S_key((--__before)._M_node)))
 	    {
-	      if (_S_right(__before._M_node) == 0)
-		return _Res(0, __before._M_node);
+	      if (!_S_right(__before._M_node))
+		return _Res(_Base_ptr(), __before._M_node);
 	      else
-		return _Res(__pos._M_node, __pos._M_node);
+		return _Res(__position._M_node, __position._M_node);
 	    }
 	  else
 	    return _M_get_insert_equal_pos(__k);
@@ -2325,18 +2943,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       else
 	{
 	  // ... then try after.
-	  iterator __after = __pos;
-	  if (__pos._M_node == _M_rightmost())
-	    return _Res(0, _M_rightmost());
+	  iterator __after(__position._M_node);
+	  if (__position._M_node == _M_rightmost())
+	    return _Res(_Base_ptr(), _M_rightmost());
 	  else if (!_M_impl._M_key_compare(_S_key((++__after)._M_node), __k))
 	    {
-	      if (_S_right(__pos._M_node) == 0)
-		return _Res(0, __pos._M_node);
+	      if (!_S_right(__position._M_node))
+		return _Res(_Base_ptr(), __position._M_node);
 	      else
 		return _Res(__after._M_node, __after._M_node);
 	    }
 	  else
-	    return _Res(0, 0);
+	    return _Res(_Base_ptr(), _Base_ptr());
 	}
     }
 
@@ -2373,46 +2991,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Link_type __z)
+    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Node_ptr __z)
     -> iterator
     {
-      bool __insert_left = (__x != 0 || __p == _M_end()
+      bool __insert_left = (__x || __p == _M_end()
 			    || _M_impl._M_key_compare(_S_key(__z),
 						      _S_key(__p)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Base_ptr __base_z = __z->_M_base_ptr();
+      _Node_traits::_S_insert_and_rebalance
+	(__insert_left, __base_z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
-      return iterator(__z);
+      return iterator(__base_z);
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_lower_node(_Base_ptr __p, _Link_type __z)
+    _M_insert_lower_node(_Base_ptr __p, _Node_ptr __z)
     -> iterator
     {
       bool __insert_left = (__p == _M_end()
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _S_key(__z)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Base_ptr __base_z = __z->_M_base_ptr();
+      _Node_traits::_S_insert_and_rebalance
+	(__insert_left, __base_z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
-      return iterator(__z);
+      return iterator(__base_z);
     }
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_equal_lower_node(_Link_type __z)
+    _M_insert_equal_lower_node(_Node_ptr __z)
     -> iterator
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = !_M_impl._M_key_compare(_S_key(__x), _S_key(__z)) ?
@@ -2487,11 +3107,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     _M_erase_aux(const_iterator __position)
     {
-      _Link_type __y =
-	static_cast<_Link_type>(_Rb_tree_rebalance_for_erase
-				(const_cast<_Base_ptr>(__position._M_node),
-				 this->_M_impl._M_header));
-      _M_drop_node(__y);
+      _Base_ptr __y = _Node_traits::_S_rebalance_for_erase
+	(__position._M_node, this->_M_impl._M_header);
+      _M_drop_node(static_cast<_Node&>(*__y)._M_node_ptr());
       --_M_impl._M_node_count;
     }
 
@@ -2527,7 +3145,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     find(const _Key& __k)
     {
-      iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
+      iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
       return (__j == end()
 	      || _M_impl._M_key_compare(__k,
 					_S_key(__j._M_node))) ? end() : __j;
@@ -2540,7 +3158,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     find(const _Key& __k) const
     {
-      const_iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
+      const_iterator __j(_M_lower_bound(_M_begin(), _M_end(), __k));
       return (__j == end()
 	      || _M_impl._M_key_compare(__k,
 					_S_key(__j._M_node))) ? end() : __j;
@@ -2574,9 +3192,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       unsigned int __len = _Rb_tree_black_count(_M_leftmost(), _M_root());
       for (const_iterator __it = begin(); __it != end(); ++__it)
 	{
-	  _Const_Link_type __x = static_cast<_Const_Link_type>(__it._M_node);
-	  _Const_Link_type __L = _S_left(__x);
-	  _Const_Link_type __R = _S_right(__x);
+	  _Base_ptr __x = __it._M_node;
+	  _Base_ptr __L = _S_left(__x);
+	  _Base_ptr __R = _S_right(__x);
 
 	  if (__x->_M_color == _S_red)
 	    if ((__L && __L->_M_color == _S_red)
@@ -2592,9 +3210,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    return false;
 	}
 
-      if (_M_leftmost() != _Rb_tree_node_base::_S_minimum(_M_root()))
+      if (_M_leftmost() != _Node_base::_S_minimum(_M_root()))
 	return false;
-      if (_M_rightmost() != _Rb_tree_node_base::_S_maximum(_M_root()))
+      if (_M_rightmost() != _Node_base::_S_maximum(_M_root()))
 	return false;
       return true;
     }
diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..3d83738f86b
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
@@ -0,0 +1,37 @@
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::map<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::map<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( v.begin()->second == 0 );
+  VERIFY( ++v.begin() == v.end() );
+  v.insert({ T { 1 }, 1 });
+  VERIFY( v.size() == 2 );
+  VERIFY( v.find(T()) != v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..41689ef43b7
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
@@ -0,0 +1,33 @@
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multimap<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::multimap<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..589e6b0a617
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
@@ -0,0 +1,32 @@
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multiset<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::multiset<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..bbcf32b20be
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
@@ -0,0 +1,32 @@
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::set<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::set<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/set/requirements/explicit_instantiation/alloc_ptr.cc b/libstdc++-v3/testsuite/23_containers/set/requirements/explicit_instantiation/alloc_ptr.cc
new file mode 100644
index 00000000000..60b583a9b2b
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/set/requirements/explicit_instantiation/alloc_ptr.cc
@@ -0,0 +1,86 @@
+// { dg-do compile { target c++11 } }
+
+#include <set>
+#include <testsuite_allocator.h>
+
+// An allocator that uses __gnu_cxx::_Pointer_adapter as its pointer type.
+template class std::set<int, std::less<int>,
+			__gnu_test::CustomPointerAlloc<int>>;
+
+// Unlike __gnu_cxx::_Pointer_adapter, this fancy pointer supports neither
+// implicit nor explicit conversions from raw pointers. The constructor from
+// a raw pointer is explicit and requires a second parameter. The only way for
+// containers to construct one of these pointers is pointer_traits::pointer_to.
+template<typename T>
+struct Pointer : __gnu_test::PointerBase<Pointer<T>, T>
+{
+  using Base = __gnu_test::PointerBase<Pointer<T>, T>;
+
+  Pointer() = default;
+  Pointer(std::nullptr_t) : Base() { }
+  explicit Pointer(T* p, int) : Base(p) { }
+
+  // Allow conversions to const_pointer and to void_pointer
+  template<typename U, typename = typename std::enable_if<
+    (!std::is_const<U>::value && std::is_same<T, const U>::value)
+    || (std::is_void<T>::value && std::is_convertible<U*, T*>::value)
+    >::type>
+    Pointer(const Pointer<U>& p) : Base(p.operator->()) { }
+
+  template<typename U>
+    static typename std::enable_if<std::is_same<U, T>::value, Pointer>::type
+    pointer_to(U& t)
+    { return Pointer(std::addressof(t), 1); }
+};
+
+// A minimal allocator that uses Pointer as its pointer type.
+template<typename T>
+struct Allocator
+{
+  using value_type = T;
+  using pointer = Pointer<T>;
+
+  Allocator() = default;
+  template<typename U>
+    Allocator(const Allocator<U>&) { }
+
+  pointer allocate(std::size_t n)
+  { return pointer(std::allocator<T>().allocate(n), 1); }
+
+  void deallocate(pointer p, std::size_t n)
+  {
+    std::allocator<T>().deallocate(p.operator->(), n);
+  }
+
+  bool operator==(const Allocator&) const { return true; }
+  bool operator!=(const Allocator&) const { return false; }
+};
+
+template class std::set<int, std::less<int>, Allocator<int>>;
+
+#include <testsuite_iterators.h>
+
+void
+test_template_members(__gnu_test::input_container<short>& c)
+{
+  // Use member functions that are not included in explicit instantiations.
+  std::set<int, std::less<int>, Allocator<int>> s(c.begin(), c.end());
+  s.emplace(1);
+  s.emplace_hint(s.begin(), 1);
+  s.insert(c.begin(), c.end());
+
+  std::set<int, std::greater<int>, Allocator<int>> s1;
+  s.merge(s1);
+  std::multiset<int, std::greater<int>, Allocator<int>> m1;
+  s.merge(m1);
+
+#ifdef __glibcxx_ranges_to_container
+  short arr[2];
+  __gnu_test::test_input_range<short> r(arr);
+  std::set<int, std::less<int>, Allocator<int>> s2(std::from_range, r);
+  s2.assign_range(r);
+  s2.prepend_range(r);
+  s2.append_range(r);
+  s2.insert_range(s2.begin(), r);
+#endif
+}
diff --git a/libstdc++-v3/testsuite/23_containers/set/requirements/explicit_instantiation/alloc_ptr_ignored.cc b/libstdc++-v3/testsuite/23_containers/set/requirements/explicit_instantiation/alloc_ptr_ignored.cc
new file mode 100644
index 00000000000..fa06cf8f3a2
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/set/requirements/explicit_instantiation/alloc_ptr_ignored.cc
@@ -0,0 +1,4 @@
+// { dg-options "-D_GLIBCXX_USE_ALLOC_PTR_FOR_RB_TREE=0" }
+// { dg-do compile { target c++11 } }
+
+#include "alloc_ptr.cc"
  
Jonathan Wakely Dec. 14, 2024, 2:59 p.m. UTC | #21
On Mon, 9 Dec 2024, 06:05 François Dumont, <frs.dumont@gmail.com> wrote:

>
> On 04/12/2024 22:48, Jonathan Wakely wrote:
> > On 04/12/24 19:27 +0100, François Dumont wrote:
> >> Hi
> >>
> >> I've completed the synchronization with your equivalent PR for
> >> std::list so here is the updated patch.
> >>
> >> PR updated a couple of days ago.
> >>
> >> Note that I've started to rework the patch for the same in _Hashtable.
> >
> > Great, thanks.
> >
> >>
> >> #if __cplusplus < 201103L
> >>       void
> >> -      _M_construct_node(_Link_type __node, const value_type& __x)
> >> +      _M_construct_node(_Base_ptr __p, const value_type& __x)
> >
> > And can this be left as _Node_ptr instead of changed to _Base_ptr?
> > Then it wouldn't need the static_cast, and the callers wouldn't need
> > to use _M_base_ptr().
> >
> > Logically, it has to be a _Node_ptr or the cast isn't valid anyway.
> >
> > There seem to be several places like this where we're giving up
> > valuable type information by using _base_ptr instead of _Node_ptr.
> >
> > Is that really necessary? If we have a _Node_ptr we can always get a
> > _Base_ptr easily by calling _M_base_ptr().
> >
> > Is it a constness thing, because _M_begin() const was changed to
> > return _Base_ptr instead of _Const_link_ptr?
> >
> No, just me being lazy and considering that as we store _Base_ptr we can
> also use it everywhere.
>
> In this new version I've preserved _Node_ptr almost everywhere.
>
> But _M_begin() is still returning a _Base_ptr and I've introduced
> _M_begin_node() for rare occasions when a _Node_ptr is needed.
>
> Note that I've hesitated to add a _S_nullptr() to get a _Base_ptr null
> pointer. Is default constructor on fancy pointer type mandatory ?


Yes, but default construction can give a singular value (like an
uninitialized int*) so you want to use value initialization.

So:

pointer p = pointer();

Or in C++11:
pointer p{};

And not just:
pointer p;


I fear
> that only constructor from nullptr is mandatory.
>

Value init is required to work by the Cpp17NullablePointer requirements.



> I've forced push on:
>
> https://forge.sourceware.org/gcc/gcc-TEST/pulls/27


Thanks, I'll take another look.
  
Jonathan Wakely Dec. 16, 2024, 4:02 p.m. UTC | #22
On Sat, 14 Dec 2024 at 14:59, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
>
>
>
> On Mon, 9 Dec 2024, 06:05 François Dumont, <frs.dumont@gmail.com> wrote:
>>
>>
>> On 04/12/2024 22:48, Jonathan Wakely wrote:
>> > On 04/12/24 19:27 +0100, François Dumont wrote:
>> >> Hi
>> >>
>> >> I've completed the synchronization with your equivalent PR for
>> >> std::list so here is the updated patch.
>> >>
>> >> PR updated a couple of days ago.
>> >>
>> >> Note that I've started to rework the patch for the same in _Hashtable.
>> >
>> > Great, thanks.
>> >
>> >>
>> >> #if __cplusplus < 201103L
>> >>       void
>> >> -      _M_construct_node(_Link_type __node, const value_type& __x)
>> >> +      _M_construct_node(_Base_ptr __p, const value_type& __x)
>> >
>> > And can this be left as _Node_ptr instead of changed to _Base_ptr?
>> > Then it wouldn't need the static_cast, and the callers wouldn't need
>> > to use _M_base_ptr().
>> >
>> > Logically, it has to be a _Node_ptr or the cast isn't valid anyway.
>> >
>> > There seem to be several places like this where we're giving up
>> > valuable type information by using _base_ptr instead of _Node_ptr.
>> >
>> > Is that really necessary? If we have a _Node_ptr we can always get a
>> > _Base_ptr easily by calling _M_base_ptr().
>> >
>> > Is it a constness thing, because _M_begin() const was changed to
>> > return _Base_ptr instead of _Const_link_ptr?
>> >
>> No, just me being lazy and considering that as we store _Base_ptr we can
>> also use it everywhere.
>>
>> In this new version I've preserved _Node_ptr almost everywhere.
>>
>> But _M_begin() is still returning a _Base_ptr and I've introduced
>> _M_begin_node() for rare occasions when a _Node_ptr is needed.
>>
>> Note that I've hesitated to add a _S_nullptr() to get a _Base_ptr null
>> pointer. Is default constructor on fancy pointer type mandatory ?
>
>
> Yes, but default construction can give a singular value (like an uninitialized int*) so you want to use value initialization.
>
> So:
>
> pointer p = pointer();
>
> Or in C++11:
> pointer p{};
>
> And not just:
> pointer p;
>
>
>> I fear
>> that only constructor from nullptr is mandatory.
>
>
> Value init is required to work by the Cpp17NullablePointer requirements.

See https://en.cppreference.com/w/cpp/named_req/NullablePointer for
the cppreference.com version of those requirements.


>
>
>>
>> I've forced push on:
>>
>> https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
>
>
> Thanks, I'll take another look.
>
>
  
Jonathan Wakely Dec. 18, 2024, 9:32 p.m. UTC | #23
On Sat, 14 Dec 2024 at 13:36, François Dumont <frs.dumont@gmail.com> wrote:
>
> Hi
>
> Here is an update with the alloc ptr specific tests and so fixes.
>
> PR updated:
>
> https://forge.sourceware.org/gcc/gcc-TEST/pulls/27

Approved on the forge. Please add PR 57272 to the commit msg and push
to trunk - thanks for all the work on this!


>
> François
>
>
> On 09/12/2024 07:05, François Dumont wrote:
> >
> > On 04/12/2024 22:48, Jonathan Wakely wrote:
> >> On 04/12/24 19:27 +0100, François Dumont wrote:
> >>> Hi
> >>>
> >>> I've completed the synchronization with your equivalent PR for
> >>> std::list so here is the updated patch.
> >>>
> >>> PR updated a couple of days ago.
> >>>
> >>> Note that I've started to rework the patch for the same in _Hashtable.
> >>
> >> Great, thanks.
> >>
> >>>
> >>> #if __cplusplus < 201103L
> >>>       void
> >>> -      _M_construct_node(_Link_type __node, const value_type& __x)
> >>> +      _M_construct_node(_Base_ptr __p, const value_type& __x)
> >>
> >> And can this be left as _Node_ptr instead of changed to _Base_ptr?
> >> Then it wouldn't need the static_cast, and the callers wouldn't need
> >> to use _M_base_ptr().
> >>
> >> Logically, it has to be a _Node_ptr or the cast isn't valid anyway.
> >>
> >> There seem to be several places like this where we're giving up
> >> valuable type information by using _base_ptr instead of _Node_ptr.
> >>
> >> Is that really necessary? If we have a _Node_ptr we can always get a
> >> _Base_ptr easily by calling _M_base_ptr().
> >>
> >> Is it a constness thing, because _M_begin() const was changed to
> >> return _Base_ptr instead of _Const_link_ptr?
> >>
> > No, just me being lazy and considering that as we store _Base_ptr we
> > can also use it everywhere.
> >
> > In this new version I've preserved _Node_ptr almost everywhere.
> >
> > But _M_begin() is still returning a _Base_ptr and I've introduced
> > _M_begin_node() for rare occasions when a _Node_ptr is needed.
> >
> > Note that I've hesitated to add a _S_nullptr() to get a _Base_ptr null
> > pointer. Is default constructor on fancy pointer type mandatory ? I
> > fear that only constructor from nullptr is mandatory.
> >
> > I've forced push on:
> >
> > https://forge.sourceware.org/gcc/gcc-TEST/pulls/27
> >
> > François
> >
  

Patch

diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
index 978093fc587..804023b9e68 100644
--- a/libstdc++-v3/include/bits/stl_tree.h
+++ b/libstdc++-v3/include/bits/stl_tree.h
@@ -94,10 +94,20 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   enum _Rb_tree_color { _S_red = false, _S_black = true };
 
-  struct _Rb_tree_node_base
+ template<typename _BasePtr, typename _BaseCPtr>
+    struct _Rb_tree_pnode_base
     {
-    typedef _Rb_tree_node_base* _Base_ptr;
-    typedef const _Rb_tree_node_base* _Const_Base_ptr;
+#if __cplusplus >= 201103L
+      using _Base_ptr =
+	__conditional_t<is_pointer<_BasePtr>::value,
+	_BasePtr, __ptr_rebind<_BasePtr, _Rb_tree_pnode_base>>;
+      using _Const_Base_ptr =
+	__conditional_t<is_pointer<_BaseCPtr>::value,
+	_BaseCPtr, __ptr_rebind<_BaseCPtr, const _Rb_tree_pnode_base>>;
+#else
+      typedef _BasePtr	_Base_ptr;
+      typedef _BaseCPtr	_Const_Base_ptr;
+#endif
 
       _Rb_tree_color	_M_color;
       _Base_ptr		_M_parent;
@@ -107,32 +117,36 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       static _Base_ptr
       _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
       {
-      while (__x->_M_left != 0) __x = __x->_M_left;
+	while (__x->_M_left) __x = __x->_M_left;
 	return __x;
       }
 
       static _Const_Base_ptr
       _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
       {
-      while (__x->_M_left != 0) __x = __x->_M_left;
+	while (__x->_M_left) __x = __x->_M_left;
 	return __x;
       }
 
       static _Base_ptr
       _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
       {
-      while (__x->_M_right != 0) __x = __x->_M_right;
+	while (__x->_M_right) __x = __x->_M_right;
 	return __x;
       }
 
       static _Const_Base_ptr
       _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
       {
-      while (__x->_M_right != 0) __x = __x->_M_right;
+	while (__x->_M_right) __x = __x->_M_right;
 	return __x;
       }
     };
 
+  struct _Rb_tree_node_base
+    : _Rb_tree_pnode_base<_Rb_tree_node_base*, const _Rb_tree_node_base*>
+  { };
+
   // Helper type offering value initialization guarantee on the compare functor.
   template<typename _Key_compare>
     struct _Rb_tree_key_compare
@@ -161,21 +175,27 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   // Helper type to manage default initialization of node count and header.
-  struct _Rb_tree_header
+  template<typename _NodeBase>
+    struct _Rb_tree_pheader
     {
-    _Rb_tree_node_base	_M_header;
+    private:
+      typedef typename _NodeBase::_Base_ptr		_Base_ptr;
+      typedef typename _NodeBase::_Const_Base_ptr	_Const_Base_ptr;
+
+    public:
+      _NodeBase		_M_header;
       size_t		_M_node_count; // Keeps track of size of tree.
 
-    _Rb_tree_header() _GLIBCXX_NOEXCEPT
+      _Rb_tree_pheader() _GLIBCXX_NOEXCEPT
       {
 	_M_header._M_color = _S_red;
 	_M_reset();
       }
 
 #if __cplusplus >= 201103L
-    _Rb_tree_header(_Rb_tree_header&& __x) noexcept
+      _Rb_tree_pheader(_Rb_tree_pheader&& __x) noexcept
       {
-      if (__x._M_header._M_parent != nullptr)
+	if (__x._M_header._M_parent)
 	  _M_move_data(__x);
 	else
 	  {
@@ -186,13 +206,13 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
       void
-    _M_move_data(_Rb_tree_header& __from)
+      _M_move_data(_Rb_tree_pheader& __from)
       {
 	_M_header._M_color = __from._M_header._M_color;
 	_M_header._M_parent = __from._M_header._M_parent;
 	_M_header._M_left = __from._M_header._M_left;
 	_M_header._M_right = __from._M_header._M_right;
-      _M_header._M_parent->_M_parent = &_M_header;
+	_M_header._M_parent->_M_parent = _M_header_ptr();
 	_M_node_count = __from._M_node_count;
 
 	__from._M_reset();
@@ -201,41 +221,89 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       void
       _M_reset()
       {
-      _M_header._M_parent = 0;
-      _M_header._M_left = &_M_header;
-      _M_header._M_right = &_M_header;
+	_M_header._M_parent = _Base_ptr();
+	_M_header._M_left = _M_header._M_right = _M_header_ptr();
 	_M_node_count = 0;
       }
+
+      _Base_ptr
+      _M_header_ptr()
+      {
+#if __cplusplus >= 201103L
+	using __base_ptr_traits = std::pointer_traits<_Base_ptr>;
+	return __base_ptr_traits::pointer_to(_M_header);
+#else
+	return &_M_header;
+#endif
+      }
+
+      _Const_Base_ptr
+      _M_header_ptr() const
+      {
+#if __cplusplus >= 201103L
+	using __base_ptr_traits = std::pointer_traits<_Const_Base_ptr>;
+	return __base_ptr_traits::pointer_to(_M_header);
+#else
+	return &_M_header;
+#endif
+      }
     };
 
+  struct _Rb_tree_header : _Rb_tree_pheader<_Rb_tree_node_base>
+  { };
+
   template<typename _Val>
-    struct _Rb_tree_node : public _Rb_tree_node_base
+    struct _Rb_tree_node_val
     {
-      typedef _Rb_tree_node<_Val>* _Link_type;
-
 #if __cplusplus < 201103L
       _Val _M_value_field;
 
+      __attribute__((__always_inline__))
       _Val*
       _M_valptr()
       { return std::__addressof(_M_value_field); }
 
+      __attribute__((__always_inline__))
       const _Val*
       _M_valptr() const
       { return std::__addressof(_M_value_field); }
 #else
       __gnu_cxx::__aligned_membuf<_Val> _M_storage;
 
+      [[__gnu__::__always_inline__]]
       _Val*
       _M_valptr()
       { return _M_storage._M_ptr(); }
 
+      [[__gnu__::__always_inline__]]
       const _Val*
       _M_valptr() const
       { return _M_storage._M_ptr(); }
 #endif
     };
 
+  template<typename _Val>
+    struct _Rb_tree_node
+    : _Rb_tree_node_base
+    , _Rb_tree_node_val<_Val>
+    {
+      typedef _Rb_tree_node<_Val>* _Link_type;
+      typedef const _Rb_tree_node<_Val>* _Const_Link_type;
+    };
+
+#if __cplusplus >= 201103L
+  template<typename _ValPtr, typename _ValConstPtr>
+    struct _Rb_tree_pnode
+    : _Rb_tree_pnode_base<_ValPtr, _ValConstPtr>
+    , _Rb_tree_node_val<typename std::pointer_traits<_ValPtr>::element_type>
+    {
+      using _Link_type =
+	__ptr_rebind<_ValPtr, _Rb_tree_pnode<_ValPtr, _ValConstPtr>>;
+      using _Const_Link_type =
+	__ptr_rebind<_ValConstPtr, const _Rb_tree_pnode<_ValPtr, _ValConstPtr>>;
+    };
+#endif
+
   _GLIBCXX_PURE _Rb_tree_node_base*
   _Rb_tree_increment(_Rb_tree_node_base* __x) throw ();
 
@@ -248,6 +316,18 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _GLIBCXX_PURE const _Rb_tree_node_base*
   _Rb_tree_decrement(const _Rb_tree_node_base* __x) throw ();
 
+  __attribute__((__nonnull__))
+  void
+  _Rb_tree_insert_and_rebalance(const bool __insert_left,
+				_Rb_tree_node_base* __x,
+				_Rb_tree_node_base* __p,
+				_Rb_tree_node_base& __header) throw ();
+
+  __attribute__((__nonnull__,__returns_nonnull__))
+  _Rb_tree_node_base*
+  _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
+			       _Rb_tree_node_base& __header) throw ();
+
   template<typename _Tp>
     struct _Rb_tree_iterator
     {
@@ -401,17 +481,567 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Base_ptr _M_node;
     };
 
+  struct _Rb_tree_helpers
+  {
+    static _Rb_tree_node_base*
+    _S_minimum(_Rb_tree_node_base* __x) _GLIBCXX_NOEXCEPT
+    { return _Rb_tree_node_base::_S_minimum(__x); }
+
+    static const _Rb_tree_node_base*
+    _S_minimum(const _Rb_tree_node_base* __x) _GLIBCXX_NOEXCEPT
+    { return _Rb_tree_node_base::_S_minimum(__x); }
+
+#if __cplusplus >= 201103L
+    template<typename _BasePtr>
+      static _BasePtr
+      _S_minimum(_BasePtr __x)
+      {
+	while (__x->_M_left) __x = __x->_M_left;
+	return __x;
+      }
+#endif
+
+    static _Rb_tree_node_base*
+    _S_maximum(_Rb_tree_node_base* __x) _GLIBCXX_NOEXCEPT
+    { return _Rb_tree_node_base::_S_maximum(__x); }
+
+    static const _Rb_tree_node_base*
+    _S_maximum(const _Rb_tree_node_base* __x) _GLIBCXX_NOEXCEPT
+    { return _Rb_tree_node_base::_S_maximum(__x); }
+
+#if __cplusplus >= 201103L
+    template<typename _BasePtr>
+      static _BasePtr
+      _S_maximum(_BasePtr __x)
+      {
+	while (__x->_M_right) __x = __x->_M_right;
+	return __x;
+      }
+#endif
+
     __attribute__((__nonnull__))
-  void
-  _Rb_tree_insert_and_rebalance(const bool __insert_left,
-				_Rb_tree_node_base* __x,
-				_Rb_tree_node_base* __p,
-				_Rb_tree_node_base& __header) throw ();
+    static void
+    _S_insert_and_rebalance(const bool __insert_left,
+			    _Rb_tree_node_base* __x, _Rb_tree_node_base* __p,
+			    _Rb_tree_node_base& __header) throw ()
+    {
+      return _Rb_tree_insert_and_rebalance
+	(__insert_left, __x, __p, __header);
+    }
 
-  __attribute__((__nonnull__,__returns_nonnull__))
-  _Rb_tree_node_base*
-  _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
-			       _Rb_tree_node_base& __header) throw ();
+    __attribute__((__nonnull__))
+    static _Rb_tree_node_base*
+    _S_rebalance_for_erase(_Rb_tree_node_base* const __z,
+			   _Rb_tree_node_base& __header) throw ()
+    { return _Rb_tree_rebalance_for_erase(__z, __header); }
+
+#if __cplusplus >= 201103L
+    template<typename _BasePtr>
+      static _BasePtr
+      _Increment(_BasePtr __x) noexcept
+      {
+	if (__x->_M_right)
+	  {
+	    __x = __x->_M_right;
+	    while (__x->_M_left)
+	      __x = __x->_M_left;
+	  }
+	else
+	  {
+	    _BasePtr __y = __x->_M_parent;
+	    while (__x == __y->_M_right)
+	      {
+		__x = __y;
+		__y = __y->_M_parent;
+	      }
+	    if (__x->_M_right != __y)
+	      __x = __y;
+	  }
+	return __x;
+      }
+
+    template<typename _BasePtr>
+      static _BasePtr
+      _Decrement(_BasePtr __x) noexcept
+      {
+	if (__x->_M_color == _S_red
+	    && __x->_M_parent->_M_parent == __x)
+	  __x = __x->_M_right;
+	else if (__x->_M_left)
+	  {
+	    _BasePtr __y = __x->_M_left;
+	    while (__y->_M_right)
+	      __y = __y->_M_right;
+	    __x = __y;
+	  }
+	else
+	  {
+	    _BasePtr __y = __x->_M_parent;
+	    while (__x == __y->_M_left)
+	      {
+		__x = __y;
+		__y = __y->_M_parent;
+	      }
+	    __x = __y;
+	  }
+	return __x;
+      }
+
+    template<typename _BasePtr>
+      static void
+      _Rotate_left(_BasePtr __x, _BasePtr& __root)
+      {
+	const _BasePtr __y = __x->_M_right;
+
+	__x->_M_right = __y->_M_left;
+	if (__y->_M_left)
+	  __y->_M_left->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_left)
+	  __x->_M_parent->_M_left = __y;
+	else
+	  __x->_M_parent->_M_right = __y;
+	__y->_M_left = __x;
+	__x->_M_parent = __y;
+      }
+
+    template<typename _BasePtr>
+      static void
+      _Rotate_right(_BasePtr __x, _BasePtr& __root)
+      {
+	const _BasePtr __y = __x->_M_left;
+
+	__x->_M_left = __y->_M_right;
+	if (__y->_M_right)
+	  __y->_M_right->_M_parent = __x;
+	__y->_M_parent = __x->_M_parent;
+
+	if (__x == __root)
+	  __root = __y;
+	else if (__x == __x->_M_parent->_M_right)
+	  __x->_M_parent->_M_right = __y;
+	else
+	  __x->_M_parent->_M_left = __y;
+	__y->_M_right = __x;
+	__x->_M_parent = __y;
+      }
+
+    template<typename _BasePtr, typename _Header>
+      static void
+      _S_insert_and_rebalance(const bool __insert_left,
+			      _BasePtr __x, _BasePtr __p,
+			      _Header& __header)
+      {
+	_BasePtr& __root = __header._M_parent;
+
+	// Initialize fields in new node to insert.
+	__x->_M_parent = __p;
+	__x->_M_left = __x->_M_right = nullptr;
+	__x->_M_color = _S_red;
+
+	// Insert.
+	// Make new node child of parent and maintain root, leftmost and
+	// rightmost nodes.
+	// N.B. First node is always inserted left.
+	if (__insert_left)
+	  {
+	    __p->_M_left = __x; // also makes leftmost = __x when __p == &__header
+
+	    if (std::__to_address(__p) == std::addressof(__header))
+	      {
+		__header._M_parent = __x;
+		__header._M_right = __x;
+	      }
+	    else if (__p == __header._M_left)
+	      __header._M_left = __x; // maintain leftmost pointing to min node
+	  }
+	else
+	  {
+	    __p->_M_right = __x;
+
+	    if (__p == __header._M_right)
+	      __header._M_right = __x; // maintain rightmost pointing to max node
+	  }
+	// Rebalance.
+	while (__x != __root
+	       && __x->_M_parent->_M_color == _S_red)
+	  {
+	    const _BasePtr __xpp = __x->_M_parent->_M_parent;
+
+	    if (__x->_M_parent == __xpp->_M_left)
+	      {
+		const _BasePtr __y = __xpp->_M_right;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_right)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_left(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_right(__xpp, __root);
+		  }
+	      }
+	    else
+	      {
+		const _BasePtr __y = __xpp->_M_left;
+		if (__y && __y->_M_color == _S_red)
+		  {
+		    __x->_M_parent->_M_color = _S_black;
+		    __y->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    __x = __xpp;
+		  }
+		else
+		  {
+		    if (__x == __x->_M_parent->_M_left)
+		      {
+			__x = __x->_M_parent;
+			_Rotate_right(__x, __root);
+		      }
+		    __x->_M_parent->_M_color = _S_black;
+		    __xpp->_M_color = _S_red;
+		    _Rotate_left(__xpp, __root);
+		  }
+	      }
+	  }
+	__root->_M_color = _S_black;
+      }
+
+    template<typename _BasePtr, typename _Header>
+      static _BasePtr
+      _S_rebalance_for_erase(_BasePtr __z, _Header& __header)
+      {
+	_BasePtr& __root = __header._M_parent;
+	_BasePtr& __leftmost = __header._M_left;
+	_BasePtr& __rightmost = __header._M_right;
+	_BasePtr __y = __z;
+	_BasePtr __x{};
+	_BasePtr __x_parent{};
+
+	if (!__y->_M_left)     // __z has at most one non-null child. y == z.
+	  __x = __y->_M_right;     // __x might be null.
+	else
+	  if (!__y->_M_right)  // __z has exactly one non-null child. y == z.
+	    __x = __y->_M_left;    // __x is not null.
+	  else
+	    {
+	      // __z has two non-null children.  Set __y to
+	      __y = __y->_M_right;   //   __z's successor.  __x might be null.
+	      while (__y->_M_left)
+		__y = __y->_M_left;
+	      __x = __y->_M_right;
+	    }
+	if (__y != __z)
+	  {
+	    // relink y in place of z.  y is z's successor
+	    __z->_M_left->_M_parent = __y;
+	    __y->_M_left = __z->_M_left;
+	    if (__y != __z->_M_right)
+	      {
+		__x_parent = __y->_M_parent;
+		if (__x)
+		  __x->_M_parent = __y->_M_parent;
+		__y->_M_parent->_M_left = __x;   // __y must be a child of _M_left
+		__y->_M_right = __z->_M_right;
+		__z->_M_right->_M_parent = __y;
+	      }
+	    else
+	      __x_parent = __y;
+	    if (__root == __z)
+	      __root = __y;
+	    else if (__z->_M_parent->_M_left == __z)
+	      __z->_M_parent->_M_left = __y;
+	    else
+	      __z->_M_parent->_M_right = __y;
+	    __y->_M_parent = __z->_M_parent;
+	    std::swap(__y->_M_color, __z->_M_color);
+	    __y = __z;
+	    // __y now points to node to be actually deleted
+	  }
+	else
+	  {                        // __y == __z
+	    __x_parent = __y->_M_parent;
+	    if (__x)
+	      __x->_M_parent = __y->_M_parent;
+	    if (__root == __z)
+	      __root = __x;
+	    else
+	      if (__z->_M_parent->_M_left == __z)
+		__z->_M_parent->_M_left = __x;
+	      else
+		__z->_M_parent->_M_right = __x;
+	    if (__leftmost == __z)
+	      {
+		if (!__z->_M_right)        // __z->_M_left must be null also
+		  __leftmost = __z->_M_parent;
+		// makes __leftmost == _M_header if __z == __root
+		else
+		  __leftmost = _S_minimum(__x);
+	      }
+	    if (__rightmost == __z)
+	      {
+		if (__z->_M_left == 0)         // __z->_M_right must be null also
+		  __rightmost = __z->_M_parent;
+		// makes __rightmost == _M_header if __z == __root
+		else                      // __x == __z->_M_left
+		  __rightmost = _S_maximum(__x);
+	      }
+	  }
+	if (__y->_M_color != _S_red)
+	  {
+	    while (__x != __root && (__x == 0 || __x->_M_color == _S_black))
+	      if (__x == __x_parent->_M_left)
+		{
+		  _BasePtr __w = __x_parent->_M_right;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_left(__x_parent, __root);
+		      __w = __x_parent->_M_right;
+		    }
+		  if ((!__w->_M_left || __w->_M_left->_M_color == _S_black) &&
+		      (!__w->_M_right || __w->_M_right->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_right || __w->_M_right->_M_color == _S_black)
+			{
+			  __w->_M_left->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_right(__w, __root);
+			  __w = __x_parent->_M_right;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_right)
+			__w->_M_right->_M_color = _S_black;
+		      _Rotate_left(__x_parent, __root);
+		      break;
+		    }
+		}
+	      else
+		{
+		  // same as above, with _M_right <-> _M_left.
+		  _BasePtr __w = __x_parent->_M_left;
+		  if (__w->_M_color == _S_red)
+		    {
+		      __w->_M_color = _S_black;
+		      __x_parent->_M_color = _S_red;
+		      _Rotate_right(__x_parent, __root);
+		      __w = __x_parent->_M_left;
+		    }
+		  if ((!__w->_M_right || __w->_M_right->_M_color == _S_black) &&
+		      (!__w->_M_left || __w->_M_left->_M_color == _S_black))
+		    {
+		      __w->_M_color = _S_red;
+		      __x = __x_parent;
+		      __x_parent = __x_parent->_M_parent;
+		    }
+		  else
+		    {
+		      if (!__w->_M_left || __w->_M_left->_M_color == _S_black)
+			{
+			  __w->_M_right->_M_color = _S_black;
+			  __w->_M_color = _S_red;
+			  _Rotate_left(__w, __root);
+			  __w = __x_parent->_M_left;
+			}
+		      __w->_M_color = __x_parent->_M_color;
+		      __x_parent->_M_color = _S_black;
+		      if (__w->_M_left)
+			__w->_M_left->_M_color = _S_black;
+		      _Rotate_right(__x_parent, __root);
+		      break;
+		    }
+		}
+	    if (__x)
+	      __x->_M_color = _S_black;
+	  }
+
+	return __y;
+      }
+#endif
+  };
+
+#if __cplusplus >= 201103L
+  template<typename _ValPtr, typename _ValCPtr>
+    struct _Rb_tree_piterator
+    {
+      using __ptr_traits = pointer_traits<_ValPtr>;
+      using value_type = typename __ptr_traits::element_type;
+      using reference = value_type&;
+      using pointer = value_type*;
+
+      typedef bidirectional_iterator_tag iterator_category;
+      typedef ptrdiff_t			 difference_type;
+
+      typedef _Rb_tree_piterator<_ValPtr, _ValCPtr>		_Self;
+      typedef _Rb_tree_pnode<_ValPtr, _ValCPtr>			_Node_type;
+      typedef typename _Node_type::_Base_ptr			_Base_ptr;
+      typedef typename _Node_type::_Link_type			_Link_type;
+
+      _Rb_tree_piterator() _GLIBCXX_NOEXCEPT
+      : _M_node() { }
+
+      explicit
+      _Rb_tree_piterator(_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      : _M_node(__x) { }
+
+      reference
+      operator*() const _GLIBCXX_NOEXCEPT
+      { return *static_cast<_Node_type&>(*_M_node)._M_valptr(); }
+
+      pointer
+      operator->() const _GLIBCXX_NOEXCEPT
+      { return static_cast<_Node_type&>(*_M_node)._M_valptr(); }
+
+      _Self&
+      operator++() _GLIBCXX_NOEXCEPT
+      {
+	_M_node = _Rb_tree_helpers::_Increment(_M_node);
+	return *this;
+      }
+
+      _Self
+      operator++(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	_M_node = _Rb_tree_helpers::_Increment(_M_node);
+	return __tmp;
+      }
+
+      _Self&
+      operator--() _GLIBCXX_NOEXCEPT
+      {
+	_M_node = _Rb_tree_helpers::_Decrement(_M_node);
+	return *this;
+      }
+
+      _Self
+      operator--(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	_M_node = _Rb_tree_helpers::_Decrement(_M_node);
+	return __tmp;
+      }
+
+      friend bool
+      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node == __y._M_node; }
+
+#if ! __cpp_lib_three_way_comparison
+      friend bool
+      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node != __y._M_node; }
+#endif
+
+      _Base_ptr _M_node;
+    };
+
+  template<typename _ValPtr, typename _ValCPtr>
+    struct _Rb_tree_const_piterator
+    {
+      using __ptr_traits = pointer_traits<_ValPtr>;
+      using value_type = typename __ptr_traits::element_type;
+      using reference = const value_type&;
+      using pointer = const value_type*;
+
+      typedef _Rb_tree_piterator<_ValPtr, _ValCPtr>		iterator;
+      typedef typename iterator::_Base_ptr			_Base_ptr;
+
+      typedef bidirectional_iterator_tag iterator_category;
+      typedef ptrdiff_t			 difference_type;
+
+      typedef _Rb_tree_const_piterator<_ValPtr, _ValCPtr>	_Self;
+      typedef _Rb_tree_pnode_base<_ValPtr, _ValCPtr>		_Base_Node_type;
+      typedef _Rb_tree_pnode<_ValPtr, _ValCPtr>			_Node_type;
+      typedef typename _Node_type::_Const_Base_ptr		_Const_Base_ptr;
+
+      _Rb_tree_const_piterator() _GLIBCXX_NOEXCEPT
+      : _M_node() { }
+
+      explicit
+      _Rb_tree_const_piterator(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
+      : _M_node(__x) { }
+
+      _Rb_tree_const_piterator(const iterator& __it) _GLIBCXX_NOEXCEPT
+      : _M_node(__it._M_node) { }
+
+      iterator
+      _M_const_cast() const _GLIBCXX_NOEXCEPT
+      {
+	return iterator(std::pointer_traits<_Base_ptr>::pointer_to
+			(const_cast<_Base_Node_type&>(*_M_node)));
+      }
+
+      reference
+      operator*() const _GLIBCXX_NOEXCEPT
+      { return *static_cast<const _Node_type&>(*_M_node)._M_valptr(); }
+
+      pointer
+      operator->() const _GLIBCXX_NOEXCEPT
+      { return static_cast<const _Node_type&>(*_M_node)._M_valptr(); }
+
+      _Self&
+      operator++() _GLIBCXX_NOEXCEPT
+      {
+	_M_node = _Rb_tree_helpers::_Increment(_M_node);
+	return *this;
+      }
+
+      _Self
+      operator++(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	_M_node = _Rb_tree_helpers::_Increment(_M_node);
+	return __tmp;
+      }
+
+      _Self&
+      operator--() _GLIBCXX_NOEXCEPT
+      {
+	_M_node = _Rb_tree_helpers::_Decrement(_M_node);
+	return *this;
+      }
+
+      _Self
+      operator--(int) _GLIBCXX_NOEXCEPT
+      {
+	_Self __tmp = *this;
+	_M_node = _Rb_tree_helpers::_Decrement(_M_node);
+	return __tmp;
+      }
+
+      friend bool
+      operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node == __y._M_node; }
+
+#if ! __cpp_lib_three_way_comparison
+      friend bool
+      operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+      { return __x._M_node != __y._M_node; }
+#endif
+
+      _Const_Base_ptr _M_node;
+    };
+#endif // C++11
 
 #if __cplusplus > 201402L
   template<typename _Tree1, typename _Cmp2>
@@ -422,16 +1052,28 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc = allocator<_Val> >
     class _Rb_tree
     {
+#if __cplusplus < 201103L
+      typedef _Rb_tree_node_base		_Node_base;
+      typedef _Rb_tree_node<_Val>		_Node_type;
+#else
+      using __alloc_traits = std::allocator_traits<_Alloc>;
+      using __val_ptr = typename __alloc_traits::pointer;
+      using __val_cptr = typename __alloc_traits::const_pointer;
+      using _Node_base = __conditional_t<std::is_pointer<__val_ptr>::value,
+	_Rb_tree_node_base, _Rb_tree_pnode_base<__val_ptr, __val_cptr>>;
+      using _Node_type = __conditional_t<std::is_pointer<__val_cptr>::value,
+	_Rb_tree_node<_Val>, _Rb_tree_pnode<__val_ptr, __val_cptr>>;
+#endif
       typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
-	rebind<_Rb_tree_node<_Val> >::other _Node_allocator;
+	rebind<_Node_type>::other _Node_allocator;
 
       typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;
 
     protected:
-      typedef _Rb_tree_node_base* 		_Base_ptr;
-      typedef const _Rb_tree_node_base* 	_Const_Base_ptr;
-      typedef _Rb_tree_node<_Val>* 		_Link_type;
-      typedef const _Rb_tree_node<_Val>*	_Const_Link_type;
+      typedef typename _Node_base::_Base_ptr 		_Base_ptr;
+      typedef typename _Node_base::_Const_Base_ptr 	_Const_Base_ptr;
+      typedef typename _Node_type::_Link_type 		_Link_type;
+      typedef typename _Node_type::_Const_Link_type	_Const_Link_type;
 
     private:
       // Functor recycling a pool of nodes and using allocation once the pool
@@ -457,13 +1099,13 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
 	~_Reuse_or_alloc_node()
-	{ _M_t._M_erase(static_cast<_Link_type>(_M_root)); }
+	{ _M_t._M_erase(_M_root); }
 
 	template<typename _Arg>
-	  _Link_type
+	  _Base_ptr
 	  operator()(_GLIBCXX_FWDREF(_Arg) __arg)
 	  {
-	    _Link_type __node = static_cast<_Link_type>(_M_extract());
+	    _Base_ptr __node = _M_extract();
 	    if (__node)
 	      {
 		_M_t._M_destroy_node(__node);
@@ -559,13 +1201,24 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return _Alloc_traits::allocate(_M_get_Node_allocator(), 1); }
 
       void
-      _M_put_node(_Link_type __p) _GLIBCXX_NOEXCEPT
-      { _Alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1); }
+      _M_put_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
+      {
+#if __cplusplus >= 201103L
+	using __ptr_traits = std::pointer_traits<_Link_type>;
+	_Node_type& __node = static_cast<_Node_type&>(*__p);
+	_Alloc_traits::deallocate
+	  (_M_get_Node_allocator(), __ptr_traits::pointer_to(__node), 1);
+#else
+	_Alloc_traits::deallocate
+	  (_M_get_Node_allocator(), static_cast<_Link_type>(__p), 1);
+#endif
+      }
 
 #if __cplusplus < 201103L
       void
-      _M_construct_node(_Link_type __node, const value_type& __x)
+      _M_construct_node(_Base_ptr __p, const value_type& __x)
       {
+	_Link_type __node = static_cast<_Link_type>(__p);
 	__try
 	  { get_allocator().construct(__node->_M_valptr(), __x); }
 	__catch(...)
@@ -585,19 +1238,20 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #else
       template<typename... _Args>
 	void
-	_M_construct_node(_Link_type __node, _Args&&... __args)
+	_M_construct_node(_Base_ptr __p, _Args&&... __args)
 	{
+	  _Node_type& __node = static_cast<_Node_type&>(*__p);
 	  __try
 	    {
-	      ::new(__node) _Rb_tree_node<_Val>;
+	      ::new(std::__addressof(__node)) _Node_type;
 	      _Alloc_traits::construct(_M_get_Node_allocator(),
-				       __node->_M_valptr(),
+				       __node._M_valptr(),
 				       std::forward<_Args>(__args)...);
 	    }
 	  __catch(...)
 	    {
-	      __node->~_Rb_tree_node<_Val>();
-	      _M_put_node(__node);
+	      __node.~_Node_type();
+	      _M_put_node(__p);
 	      __throw_exception_again;
 	    }
 	}
@@ -613,41 +1267,48 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
       void
-      _M_destroy_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_destroy_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
       {
 #if __cplusplus < 201103L
-	get_allocator().destroy(__p->_M_valptr());
+	get_allocator().destroy(static_cast<_Link_type>(__p)->_M_valptr());
 #else
-	_Alloc_traits::destroy(_M_get_Node_allocator(), __p->_M_valptr());
-	__p->~_Rb_tree_node<_Val>();
+	_Node_type& __node = static_cast<_Node_type&>(*__p);
+	_Alloc_traits::destroy(_M_get_Node_allocator(), __node._M_valptr());
+	__node.~_Node_type();
 #endif
       }
 
       void
-      _M_drop_node(_Link_type __p) _GLIBCXX_NOEXCEPT
+      _M_drop_node(_Base_ptr __p) _GLIBCXX_NOEXCEPT
       {
 	_M_destroy_node(__p);
 	_M_put_node(__p);
       }
 
       template<bool _MoveValue, typename _NodeGen>
-	_Link_type
-	_M_clone_node(_Link_type __x, _NodeGen& __node_gen)
+	_Base_ptr
+	_M_clone_node(_Base_ptr __x, _NodeGen& __node_gen)
 	{
 #if __cplusplus >= 201103L
 	  using _Vp = __conditional_t<_MoveValue,
 				      value_type&&,
 				      const value_type&>;
 #endif
-	  _Link_type __tmp
-	    = __node_gen(_GLIBCXX_FORWARD(_Vp, *__x->_M_valptr()));
+	  _Base_ptr __tmp = __node_gen
+	    (_GLIBCXX_FORWARD(_Vp, *static_cast<_Node_type&>(*__x)._M_valptr()));
 	  __tmp->_M_color = __x->_M_color;
-	  __tmp->_M_left = 0;
-	  __tmp->_M_right = 0;
+	  __tmp->_M_left = __tmp->_M_right = _Base_ptr();
 	  return __tmp;
 	}
 
     protected:
+#if __cplusplus >= 201103L
+      using _Header_t = __conditional_t<std::is_pointer<__val_ptr>::value,
+			  _Rb_tree_header, _Rb_tree_pheader<_Node_base>>;
+#else
+      typedef _Rb_tree_header _Header_t;
+#endif
+
 #if _GLIBCXX_INLINE_VERSION
       template<typename _Key_compare>
 #else
@@ -658,7 +1319,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	struct _Rb_tree_impl
 	: public _Node_allocator
 	, public _Rb_tree_key_compare<_Key_compare>
-	, public _Rb_tree_header
+	, public _Header_t
 	{
 	  typedef _Rb_tree_key_compare<_Key_compare> _Base_key_compare;
 
@@ -672,7 +1333,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Rb_tree_impl(const _Rb_tree_impl& __x)
 	  : _Node_allocator(_Alloc_traits::_S_select_on_copy(__x))
 	  , _Base_key_compare(__x._M_key_compare)
-	  , _Rb_tree_header()
+	  , _Header_t()
 	  { }
 
 #if __cplusplus < 201103L
@@ -692,7 +1353,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Rb_tree_impl(_Rb_tree_impl&& __x, _Node_allocator&& __a)
 	  : _Node_allocator(std::move(__a)),
 	    _Base_key_compare(std::move(__x)),
-	    _Rb_tree_header(std::move(__x))
+	    _Header_t(std::move(__x))
 	  { }
 
 	  _Rb_tree_impl(const _Key_compare& __comp, _Node_allocator&& __a)
@@ -728,31 +1389,31 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_rightmost() const _GLIBCXX_NOEXCEPT
       { return this->_M_impl._M_header._M_right; }
 
-      _Link_type
+      _Base_ptr
       _M_mbegin() const _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(this->_M_impl._M_header._M_parent); }
+      {
+	return const_cast<_Rb_tree_impl<_Compare>&>
+	  (this->_M_impl)._M_header._M_parent;
+      }
 
-      _Link_type
+      _Base_ptr
       _M_begin() _GLIBCXX_NOEXCEPT
       { return _M_mbegin(); }
 
-      _Const_Link_type
+      _Const_Base_ptr
       _M_begin() const _GLIBCXX_NOEXCEPT
-      {
-	return static_cast<_Const_Link_type>
-	  (this->_M_impl._M_header._M_parent);
-      }
+      { return this->_M_impl._M_header._M_parent; }
 
       _Base_ptr
       _M_end() _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
+      { return this->_M_impl._M_header_ptr(); }
 
       _Const_Base_ptr
       _M_end() const _GLIBCXX_NOEXCEPT
-      { return &this->_M_impl._M_header; }
+      { return this->_M_impl._M_header_ptr(); }
 
       static const _Key&
-      _S_key(_Const_Link_type __x)
+      _S_key(_Const_Base_ptr __x)
       {
 #if __cplusplus >= 201103L
 	// If we're asking for the key we're presumably using the comparison
@@ -770,48 +1431,53 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 # endif // C++17
 #endif // C++11
 
-	return _KeyOfValue()(*__x->_M_valptr());
+	return _KeyOfValue()(*static_cast<const _Node_type&>(*__x)._M_valptr());
       }
 
-      static _Link_type
+      static _Base_ptr
       _S_left(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_left); }
+      { return __x->_M_left; }
 
-      static _Const_Link_type
+      static _Const_Base_ptr
       _S_left(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_left); }
+      { return __x->_M_left; }
 
-      static _Link_type
+      static _Base_ptr
       _S_right(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Link_type>(__x->_M_right); }
+      { return __x->_M_right; }
 
-      static _Const_Link_type
+      static _Const_Base_ptr
       _S_right(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return static_cast<_Const_Link_type>(__x->_M_right); }
-
-      static const _Key&
-      _S_key(_Const_Base_ptr __x)
-      { return _S_key(static_cast<_Const_Link_type>(__x)); }
+      { return __x->_M_right; }
 
       static _Base_ptr
       _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
+      { return _Rb_tree_helpers::_S_minimum(__x); }
 
       static _Const_Base_ptr
       _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_minimum(__x); }
+      { return _Rb_tree_helpers::_S_minimum(__x); }
 
       static _Base_ptr
       _S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
+      { return _Rb_tree_helpers::_S_maximum(__x); }
 
       static _Const_Base_ptr
       _S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT
-      { return _Rb_tree_node_base::_S_maximum(__x); }
+      { return _Rb_tree_helpers::_S_maximum(__x); }
 
     public:
+#if __cplusplus >= 201103L
+      using iterator = __conditional_t<std::is_pointer<__val_ptr>::value,
+	_Rb_tree_iterator<value_type>,
+	_Rb_tree_piterator<__val_ptr, __val_cptr>>;
+      using const_iterator = __conditional_t<std::is_pointer<__val_cptr>::value,
+	_Rb_tree_const_iterator<value_type>,
+	_Rb_tree_const_piterator<__val_ptr, __val_cptr>>;
+#else
       typedef _Rb_tree_iterator<value_type>       iterator;
       typedef _Rb_tree_const_iterator<value_type> const_iterator;
+#endif
 
       typedef std::reverse_iterator<iterator>       reverse_iterator;
       typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
@@ -844,7 +1510,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_(_Base_ptr __x, _Base_ptr __y, _Arg&& __v, _NodeGen&);
 
       iterator
-      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Link_type __z);
+      _M_insert_node(_Base_ptr __x, _Base_ptr __y, _Base_ptr __z);
 
       template<typename _Arg>
 	iterator
@@ -855,10 +1521,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_insert_equal_lower(_Arg&& __x);
 
       iterator
-      _M_insert_lower_node(_Base_ptr __p, _Link_type __z);
+      _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z);
 
       iterator
-      _M_insert_equal_lower_node(_Link_type __z);
+      _M_insert_equal_lower_node(_Base_ptr __z);
 #else
       template<typename _NodeGen>
 	iterator
@@ -877,22 +1543,22 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       enum { __as_lvalue, __as_rvalue };
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
-	_M_copy(_Link_type, _Base_ptr, _NodeGen&);
+	_Base_ptr
+	_M_copy(_Base_ptr, _Base_ptr, _NodeGen&);
 
       template<bool _MoveValues, typename _NodeGen>
-	_Link_type
+	_Base_ptr
 	_M_copy(const _Rb_tree& __x, _NodeGen& __gen)
 	{
-	  _Link_type __root =
+	  _Base_ptr __root =
 	    _M_copy<_MoveValues>(__x._M_mbegin(), _M_end(), __gen);
-	  _M_leftmost() = _S_minimum(__root);
-	  _M_rightmost() = _S_maximum(__root);
+	  _M_leftmost() = _Rb_tree_helpers::_S_minimum(__root);
+	  _M_rightmost() = _Rb_tree_helpers::_S_maximum(__root);
 	  _M_impl._M_node_count = __x._M_impl._M_node_count;
 	  return __root;
 	}
 
-      _Link_type
+      _Base_ptr
       _M_copy(const _Rb_tree& __x)
       {
 	_Alloc_node __an(*this);
@@ -900,22 +1566,22 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       void
-      _M_erase(_Link_type __x);
+      _M_erase(_Base_ptr __x);
 
       iterator
-      _M_lower_bound(_Link_type __x, _Base_ptr __y,
+      _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k);
 
       const_iterator
-      _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _M_lower_bound(_Const_Base_ptr __x, _Const_Base_ptr __y,
 		     const _Key& __k) const;
 
       iterator
-      _M_upper_bound(_Link_type __x, _Base_ptr __y,
+      _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		     const _Key& __k);
 
       const_iterator
-      _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+      _M_upper_bound(_Const_Base_ptr __x, _Const_Base_ptr __y,
 		     const _Key& __k) const;
 
     public:
@@ -933,7 +1599,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x)
       : _M_impl(__x._M_impl)
       {
-	if (__x._M_root() != 0)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -945,7 +1611,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(const _Rb_tree& __x, const allocator_type& __a)
       : _M_impl(__x._M_impl._M_key_compare, _Node_allocator(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_root() = _M_copy(__x);
       }
 
@@ -964,7 +1630,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Rb_tree(_Rb_tree&& __x, _Node_allocator&& __a, false_type)
       : _M_impl(__x._M_impl._M_key_compare, std::move(__a))
       {
-	if (__x._M_root() != nullptr)
+	if (__x._M_root())
 	  _M_move_data(__x, false_type{});
       }
 
@@ -999,11 +1665,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       iterator
       end() _GLIBCXX_NOEXCEPT
-      { return iterator(&this->_M_impl._M_header); }
+      { return iterator(this->_M_impl._M_header_ptr()); }
 
       const_iterator
       end() const _GLIBCXX_NOEXCEPT
-      { return const_iterator(&this->_M_impl._M_header); }
+      { return const_iterator(this->_M_impl._M_header_ptr()); }
 
       reverse_iterator
       rbegin() _GLIBCXX_NOEXCEPT
@@ -1528,10 +2194,12 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       node_type
       extract(const_iterator __pos)
       {
-	auto __ptr = _Rb_tree_rebalance_for_erase(
-	    __pos._M_const_cast()._M_node, _M_impl._M_header);
+	auto __ptr = _Rb_tree_helpers::_S_rebalance_for_erase
+	  (__pos._M_const_cast()._M_node, _M_impl._M_header);
 	--_M_impl._M_node_count;
-	return { static_cast<_Link_type>(__ptr), _M_get_Node_allocator() };
+	using __ptr_traits = std::pointer_traits<_Link_type>;
+	_Node_type& __node = static_cast<_Node_type&>(*__ptr);
+	return { __ptr_traits::pointer_to(__node), _M_get_Node_allocator() };
       }
 
       /// Extract a node.
@@ -1565,11 +2233,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr =
+		    _Rb_tree_helpers::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
-		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+		  _M_insert_node(__res.first, __res.second, __ptr);
 		}
 	    }
 	}
@@ -1587,11 +2255,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      if (__res.second)
 		{
 		  auto& __src_impl = _Merge_helper::_S_get_impl(__src);
-		  auto __ptr = _Rb_tree_rebalance_for_erase(
-		      __pos._M_node, __src_impl._M_header);
+		  auto __ptr =
+		    _Rb_tree_helpers::_S_rebalance_for_erase
+		    (__pos._M_node, __src_impl._M_header);
 		  --__src_impl._M_node_count;
-		  _M_insert_node(__res.first, __res.second,
-				 static_cast<_Link_type>(__ptr));
+		  _M_insert_node(__res.first, __res.second, __ptr);
 		}
 	    }
 	}
@@ -1702,7 +2370,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_move_assign(_Rb_tree& __x, true_type)
     {
       clear();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	_M_move_data(__x, true_type());
       std::__alloc_on_move(_M_get_Node_allocator(),
 			   __x._M_get_Node_allocator());
@@ -1721,7 +2389,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // structure.
       _Reuse_or_alloc_node __roan(*this);
       _M_impl._M_reset();
-      if (__x._M_root() != nullptr)
+      if (__x._M_root())
 	{
 	  _M_root() = _M_copy<__as_rvalue>(__x, __roan);
 	  __x.clear();
@@ -1796,7 +2464,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Reuse_or_alloc_node __roan(*this);
 	  _M_impl._M_reset();
 	  _M_impl._M_key_compare = __x._M_impl._M_key_compare;
-	  if (__x._M_root() != 0)
+	  if (__x._M_root())
 	    _M_root() = _M_copy<__as_lvalue>(__x, __roan);
 	}
 
@@ -1824,10 +2492,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 			      || _M_impl._M_key_compare(_KeyOfValue()(__v),
 							_S_key(__p)));
 
-	_Link_type __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
+	_Base_ptr __z = __node_gen(_GLIBCXX_FORWARD(_Arg, __v));
 
-	_Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				      this->_M_impl._M_header);
+	_Rb_tree_helpers::_S_insert_and_rebalance
+	  (__insert_left, __z, __p, this->_M_impl._M_header);
 	++_M_impl._M_node_count;
 	return iterator(__z);
       }
@@ -1849,10 +2517,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _KeyOfValue()(__v)));
 
-      _Link_type __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
+      _Base_ptr __z = _M_create_node(_GLIBCXX_FORWARD(_Arg, __v));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Rb_tree_helpers::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -1870,7 +2538,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_insert_equal_lower(const _Val& __v)
 #endif
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       while (__x != 0)
 	{
@@ -1884,12 +2552,12 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Key, typename _Val, typename _KoV,
 	   typename _Compare, typename _Alloc>
     template<bool _MoveValues, typename _NodeGen>
-      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Link_type
+      typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Base_ptr
       _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::
-      _M_copy(_Link_type __x, _Base_ptr __p, _NodeGen& __node_gen)
+      _M_copy(_Base_ptr __x, _Base_ptr __p, _NodeGen& __node_gen)
       {
 	// Structural copy. __x and __p must be non-null.
-	_Link_type __top = _M_clone_node<_MoveValues>(__x, __node_gen);
+	_Base_ptr __top = _M_clone_node<_MoveValues>(__x, __node_gen);
 	__top->_M_parent = __p;
 
 	__try
@@ -1902,7 +2570,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 	    while (__x != 0)
 	      {
-		_Link_type __y = _M_clone_node<_MoveValues>(__x, __node_gen);
+		_Base_ptr __y = _M_clone_node<_MoveValues>(__x, __node_gen);
 		__p->_M_left = __y;
 		__y->_M_parent = __p;
 		if (__x->_M_right)
@@ -1924,13 +2592,13 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     void
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_erase(_Link_type __x)
+    _M_erase(_Base_ptr __x)
     {
       // Erase without rebalancing.
       while (__x != 0)
 	{
 	  _M_erase(_S_right(__x));
-	  _Link_type __y = _S_left(__x);
+	  _Base_ptr __y = _S_left(__x);
 	  _M_drop_node(__x);
 	  __x = __y;
 	}
@@ -1941,10 +2609,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
 		      _Compare, _Alloc>::iterator
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Link_type __x, _Base_ptr __y,
+    _M_lower_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k)
     {
-      while (__x != 0)
+      while (__x)
 	if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	  __y = __x, __x = _S_left(__x);
 	else
@@ -1957,10 +2625,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
 		      _Compare, _Alloc>::const_iterator
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_lower_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_lower_bound(_Const_Base_ptr __x, _Const_Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (!_M_impl._M_key_compare(_S_key(__x), __k))
 	  __y = __x, __x = _S_left(__x);
 	else
@@ -1973,10 +2641,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
 		      _Compare, _Alloc>::iterator
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Link_type __x, _Base_ptr __y,
+    _M_upper_bound(_Base_ptr __x, _Base_ptr __y,
 		   const _Key& __k)
     {
-      while (__x != 0)
+      while (__x)
 	if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	  __y = __x, __x = _S_left(__x);
 	else
@@ -1989,10 +2657,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     typename _Rb_tree<_Key, _Val, _KeyOfValue,
 		      _Compare, _Alloc>::const_iterator
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_upper_bound(_Const_Link_type __x, _Const_Base_ptr __y,
+    _M_upper_bound(_Const_Base_ptr __x, _Const_Base_ptr __y,
 		   const _Key& __k) const
     {
-      while (__x != 0)
+      while (__x)
 	if (_M_impl._M_key_compare(__k, _S_key(__x)))
 	  __y = __x, __x = _S_left(__x);
 	else
@@ -2009,9 +2677,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k)
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  if (_M_impl._M_key_compare(_S_key(__x), __k))
 	    __x = _S_right(__x);
@@ -2019,7 +2687,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Link_type __xu(__x);
+	      _Base_ptr __xu(__x);
 	      _Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
@@ -2041,7 +2709,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     equal_range(const _Key& __k) const
     {
-      _Const_Link_type __x = _M_begin();
+      _Const_Base_ptr __x = _M_begin();
       _Const_Base_ptr __y = _M_end();
       while (__x != 0)
 	{
@@ -2051,7 +2719,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __y = __x, __x = _S_left(__x);
 	  else
 	    {
-	      _Const_Link_type __xu(__x);
+	      _Const_Base_ptr __xu(__x);
 	      _Const_Base_ptr __yu(__y);
 	      __y = __x, __x = _S_left(__x);
 	      __xu = _S_right(__xu);
@@ -2105,10 +2773,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_unique_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       bool __comp = true;
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __comp = _M_impl._M_key_compare(__k, _S_key(__x));
@@ -2137,9 +2805,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _M_get_insert_equal_pos(const key_type& __k)
     {
       typedef pair<_Base_ptr, _Base_ptr> _Res;
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
-      while (__x != 0)
+      while (__x)
 	{
 	  __y = __x;
 	  __x = _M_impl._M_key_compare(__k, _S_key(__x)) ?
@@ -2371,15 +3039,15 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Link_type __z)
+    _M_insert_node(_Base_ptr __x, _Base_ptr __p, _Base_ptr __z)
     -> iterator
     {
       bool __insert_left = (__x != 0 || __p == _M_end()
 			    || _M_impl._M_key_compare(_S_key(__z),
 						      _S_key(__p)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Rb_tree_helpers::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -2388,15 +3056,15 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_lower_node(_Base_ptr __p, _Link_type __z)
+    _M_insert_lower_node(_Base_ptr __p, _Base_ptr __z)
     -> iterator
     {
       bool __insert_left = (__p == _M_end()
 			    || !_M_impl._M_key_compare(_S_key(__p),
 						       _S_key(__z)));
 
-      _Rb_tree_insert_and_rebalance(__insert_left, __z, __p,
-				    this->_M_impl._M_header);
+      _Rb_tree_helpers::_S_insert_and_rebalance
+	(__insert_left, __z, __p, this->_M_impl._M_header);
       ++_M_impl._M_node_count;
       return iterator(__z);
     }
@@ -2405,10 +3073,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Compare, typename _Alloc>
     auto
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
-    _M_insert_equal_lower_node(_Link_type __z)
+    _M_insert_equal_lower_node(_Base_ptr __z)
     -> iterator
     {
-      _Link_type __x = _M_begin();
+      _Base_ptr __x = _M_begin();
       _Base_ptr __y = _M_end();
       while (__x != 0)
 	{
@@ -2485,10 +3153,8 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
     _M_erase_aux(const_iterator __position)
     {
-      _Link_type __y =
-	static_cast<_Link_type>(_Rb_tree_rebalance_for_erase
-				(const_cast<_Base_ptr>(__position._M_node),
-				 this->_M_impl._M_header));
+      _Base_ptr __y = _Rb_tree_helpers::_S_rebalance_for_erase
+	(__position._M_const_cast()._M_node, this->_M_impl._M_header);
       _M_drop_node(__y);
       --_M_impl._M_node_count;
     }
@@ -2590,9 +3256,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    return false;
 	}
 
-      if (_M_leftmost() != _Rb_tree_node_base::_S_minimum(_M_root()))
+      if (_M_leftmost() != _Rb_tree_helpers::_S_minimum(_M_root()))
 	return false;
-      if (_M_rightmost() != _Rb_tree_node_base::_S_maximum(_M_root()))
+      if (_M_rightmost() != _Rb_tree_helpers::_S_maximum(_M_root()))
 	return false;
       return true;
     }
diff --git a/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..7bef7df2f47
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/map/allocator/ext_ptr.cc
@@ -0,0 +1,40 @@ 
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct H
+{
+  std::size_t
+  operator()(const T& t) const noexcept
+  { return t.i; }
+};
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::map<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::map<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..1a9e198531f
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multimap/allocator/ext_ptr.cc
@@ -0,0 +1,40 @@ 
+// { dg-do run { target c++11 } }
+
+#include <map>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct H
+{
+  std::size_t
+  operator()(const T& t) const noexcept
+  { return t.i; }
+};
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multimap<T, int, L,
+			CustomPointerAlloc<std::pair<const T, int>>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<std::pair<const T, int>> alloc_type;
+  typedef std::multimap<T, int, L, alloc_type> test_type;
+  test_type v;
+  v.insert({ T(), 0 });
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..d4796e37672
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/multiset/allocator/ext_ptr.cc
@@ -0,0 +1,39 @@ 
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct H
+{
+  std::size_t
+  operator()(const T& t) const noexcept
+  { return t.i; }
+};
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::multiset<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::multiset<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
new file mode 100644
index 00000000000..1cf479083eb
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/set/allocator/ext_ptr.cc
@@ -0,0 +1,39 @@ 
+// { dg-do run { target c++11 } }
+
+#include <set>
+#include <memory>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct T { int i; };
+
+bool operator<(const T& l, const T& r)
+{ return l.i < r.i; }
+
+struct H
+{
+  std::size_t
+  operator()(const T& t) const noexcept
+  { return t.i; }
+};
+
+struct L : std::less<T>
+{ };
+
+using __gnu_test::CustomPointerAlloc;
+
+template class std::set<T, L, CustomPointerAlloc<T>>;
+
+void test01()
+{
+  typedef CustomPointerAlloc<T> alloc_type;
+  typedef std::set<T, L, alloc_type> test_type;
+  test_type v;
+  v.insert(T());
+  VERIFY( ++v.begin() == v.end() );
+}
+
+int main()
+{
+  test01();
+}