Will using Template Typdefs force us to duplicate iterator objects in C++23 even though "Deducing this" eliminates this problem?

129 Views Asked by At

One of the problems Deducing this solves is duplication by making member functions cvref aware of the cvref-ness of the object the function is being called on. By declaring Alias templates (Template Typdefs) and returning them in the member functions rather than just returning auto are we not forced to have two iterator objects?

template <typename T>
struct const_list_iterator{

    typedef const T& reference;    
    typedef const T* pointer;

    reference operator*() const{/***/}

    pointer operator ->() const{/***/}
 };

and

 template <typename T >
 struct list_iterator{

    typedef T& reference;    
    typedef T* pointer;

    reference operator*(){/***/}

    pointer operator ->(){/***/}
 };

However, with Deducing this if the return type is auto can we simply have a single iterator object?

template <typename T>
struct list_iterator 
    
    template <typename itself>
    auto operator*(this itself&& self){/***/}

    template <typename itself>
    auto operator->(this itself&& self){/***/}
 };

Which we can use as below:

 template <typename T>
 struct list{
     using iterator = list_iterator<T>;

      auto begin() -> iterator {
          iterator(node -> next); 
      }

      auto begin() const -> iterator{
          iterator(node -> next)
      }

 };

So, will using typedefs force us to duplicate iterator objects?

1

There are 1 best solutions below

7
Nicol Bolas On

Your example code does not propagate const correctly from the container to the iterator. begin() const will return the same type as begin(). So any deduction on the operator* of the iterator is irrelevant; the fact that const was involved has already been lost.

In order to propagate the const from the container's begin to the iterator, begin must return a different type when the container is called via const. It could be a different specialization of the same iterator template (list_iterator<const T>, for example), but it needs to be a different type.

You can give begin an explicit object parameter and deduce the const-ness from it. But it'd just be doing this:

template <typename Self>
auto begin(this Self&& self)
{
  if constexpr(std::is_const_v<Self>)
    return list_iterator<const T>(...);
  else
    return list_iterator<T>(...);
}

If the if constexpr condition bothers you, you can stick it in a helper type metafunction:

template<typename T, typename U>
  requires std::is_const_v<U>
struct propagate_const { using type = const T; };

template<typename T, typename U>
struct propagate_const { using type = T; };

template<typename T, typename U>
using propagate_const_t = propagate_const<T, U>::type;

template <typename Self>
auto begin(this Self&& self)
{
  return list_iterator<propagate_const_t<T, Self>>(...);
}