Search code examples
c++containersc++17type-traitsconst-iterator

c++ allocator traits reference and const_reference missing and iterator to const iterator conversion


I am trying to implement a custom std compliant container class with an iterator.
To do so, i started to define the public type definitions to be used by traits for the container class as well as the iterator.
I want to make it c++17 compliant and try to not use c++20 removed features or c++17 deprecated.
This is what i got:

template <typename T, typename Alloc = std::allocator<T>>
class container {
    using allocator_type = Alloc;
    using value_type = std::allocator_traits<Alloc>::value_type;
    using pointer = std::allocator_traits<Alloc>::pointer;
    using const_pointer = std::allocator_traits<Alloc>::const_pointer;
    using reference = value_type&; // <-- here 
    using const_reference = const value_type&; // <-- here 
    using difference_type = std::allocator_traits<Alloc>::difference_type;
    using size_type = std::allocator_traits<Alloc>::size_type;

    class iterator;
};

template <typename T, typename Alloc = std::allocator<T>>
class container::iterator {
    using value_type = std::allocator_traits<Alloc>::value_type;
    using pointer = std::allocator_traits<Alloc>::pointer;
    using const_pointer = std::allocator_traits<Alloc>::const_pointer;
    using reference = value_type&; // <-- here 
    using const_reference = const value_type&; // <-- here
    using difference_type = std::allocator_traits<Alloc>::difference_type;
    using size_type = std::allocator_traits<Alloc>::size_type;
};

Do i have to define reference and const_reference types myself like i did in the example or is there another std way of doint it? Another question would be how to define a const_iterator without duplicating my iterator.
Some say that i should template my iterator with the value type, some just write a new iterator.
If i would template i dont know how i would create the right type traits for it, so my function definitions for the std:
reference operator*() const and pointer operator->() const,
because reference would be const_reference and const_reference would be technically const const_reference.
For example:

template <typename T>
class container {
    
    template <typename ValueType>
    class iterator;

    using iterator = iterator<T>;
    using const_iterator = iterator<const T>;
};

template <typename ValueType>
class container::iterator {
public:
    using value_type = ValueType;
    using pointer = value_type*;
    using const_pointer = const value_type*;
    using reference = value_type&;
    using const_reference = const value_type&;

    reference operator*() const;
    pointer operator->() const;
};

Solution

  • Do i have to define reference and const_reference types myself like i did in the example

    Yes. The allocator doesn't know how your container is implemented, so it cannot know how to define those type aliases.

    Another question would be how to define a const_iterator without duplicating my iterator.

    With the magic of templates. iterator<T> vs iterator<const T>.

    because reference would be const_reference and const_reference would be technically const const_reference

    That's not a problem as long as const_reference is const T& because due to const folding rules const const_reference is simply const T& which is what you would want.