Counterintuitive std::pointer_traits rebind

François Dumont frs.dumont@gmail.com
Fri Jul 5 20:09:27 GMT 2024


Hi

I started to add support for allocator fancy pointer types in _Rb_tree 
and I wrote something like that:

   template<typename _BasePtr, typename _BaseCPtr>
     struct _Rb_tree_pnode_base
     {
#if __cplusplus < 201103L
       typedef _BasePtr        _Base_ptr;
       typedef _BaseCPtr    _Const_Base_ptr;
#else
       using _Base_ptr =
     __ptr_rebind<_BasePtr, _Rb_tree_pnode_base>;
       using _Const_Base_ptr =
     __ptr_rebind<_BaseCPtr, _Rb_tree_pnode_base>;
#endif

and then:

   struct _Rb_tree_node_base
     : _Rb_tree_pnode_base<_Rb_tree_node_base*, const _Rb_tree_node_base*>
   { };

Surprisingly I had problems with overloads like those:

       static _Base_ptr
       _S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT

       static _Const_Base_ptr
       _S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT

because std::pointer_traits<_Tp*> rebind<_Up> is defined as _Up*, 
regardless of _Tp qualifiers. So _Base_ptr and _Const_Base_ptr are the same.

Is it expected ? I find it counter-intuitive because if the allocator 
pointer and const_pointer are fancy pointers then I need to do a 
__ptr_rebind on those whereas if they are raw pointers I need to do 
something like:

using _Const_Base_ptr = __ptr_rebind<_BaseCPtr, const _Rb_tree_pnode_base>;

Unless I should do this whatever is _BaseCPtr ?

Attached is a small patch for std::pointer_traits to do what I was 
expecting.

Note that all that is hypothetical and do not reflect what I have 
finally done.

François
-------------- next part --------------
diff --git a/libstdc++-v3/include/bits/ptr_traits.h b/libstdc++-v3/include/bits/ptr_traits.h
index ca67feecca3..a1171102db2 100644
--- a/libstdc++-v3/include/bits/ptr_traits.h
+++ b/libstdc++-v3/include/bits/ptr_traits.h
@@ -177,6 +177,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct pointer_traits : __ptr_traits_impl<_Ptr, __ptr_traits_elem_t<_Ptr>>
     { };
 
+  template<typename _Tp, typename _Up>
+    using __add_const_if_t = __conditional_t<
+      is_const<_Tp>::value, typename add_const<_Up>::type, _Up>;
+
+  template<typename _Tp, typename _Up>
+    using __add_volatile_if_t = __conditional_t<
+      is_volatile<_Tp>::value, typename add_volatile<_Up>::type, _Up>;
+
+  template<typename _Tp, typename _Up>
+    using __add_cv_if_t =
+      __add_const_if_t<_Tp, __add_volatile_if_t<_Tp, _Up>>;
+
   /**
    * @brief  Partial specialization for built-in pointers.
    * @headerfile memory
@@ -193,7 +205,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       /// Type used to represent the difference between two pointers
       typedef ptrdiff_t difference_type;
       /// A pointer to a different type.
-      template<typename _Up> using rebind = _Up*;
+      template<typename _Up> using rebind =
+	__add_cv_if_t<_Tp, __decay_t<_Up>>*;
     };
 
   /// Convenience alias for rebinding pointers.


More information about the Libstdc++ mailing list