Using std::conditional with iterator

Viewed 131

In "Mastering the C++17 STL" book I saw both iterator and const_iterator implementation in one class using conditional for less code duplication

Here's my implementation for simple array class (most code for array class is skipped):

template<class T, size_t N>
class Array
{
public:
    template<bool Const>
    class ArrayIterator {
        friend class Array;
    public:
        using difference_type = std::ptrdiff_t;
        using value_type = T;
        using pointer = std::conditional<Const, const value_type*, value_type*>;
        using reference = std::conditional<Const, const value_type&, value_type&>;
        using iterator_category = std::random_access_iterator_tag;

        reference operator*() const { return *ptr; }
        ArrayIterator<Const>& operator++() { ++ptr; return *this; }
        ArrayIterator<Const> operator++(int) { auto res = *this; ++(*this); return res; }

        template<bool R>
        bool operator==(const ArrayIterator<R>& iter) const { return ptr == iter.ptr; }
        template<bool R>
        bool operator!=(const ArrayIterator<R>& iter) const { return ptr != iter.ptr; }

    private:
        explicit ArrayIterator(pointer p) : ptr(p) {};
        pointer ptr;
    };

    using iterator = ArrayIterator<false>;
    using const_iterator = ArrayIterator<true>;
    iterator begin() { return iterator(data); }
    iterator end() { return iterator(data + N); }
    const_iterator cbegin() const { return const_iterator(data); }
    const_iterator cend() const { return const_iterator(data + N); }

private:
    T* data;
};

This code compiles with no errors, but iterator is kinda unusable:

Array<int, 100> arr;
/*filling it with numbers*/
int x = *arr.begin(); 

Gives error:

main.cpp:9:9: error: no viable conversion from 'Array<int, 100>::ArrayIterator<false>::reference' (aka 'conditional<false, const int &, int &>') to 'int'

How can I use that iterator or should I just abandon this idea from book?

1 Answers

The member type pointer and reference of ArrayIterator should be defined as member type type of std::conditional, not std::conditional itself.

Change them to:

using pointer = typename std::conditional<Const, const value_type*, value_type*>::type;
//              ^^^^^^^^                                                        ^^^^^^
using reference = typename std::conditional<Const, const value_type&, value_type&>::type;
//                ^^^^^^^^                                                        ^^^^^^

Or (since C++14)

using pointer = std::conditional_t<Const, const value_type*, value_type*>;
//                              ^^  
using reference = std::conditional_t<Const, const value_type&, value_type&>;
//                                ^^  
Related