dimanche 26 juin 2016

const iterator dependent on begin() function

I tried to simplify the situation as good as possible.

Basically I have a class that represents a dynamic Array (here shown as class Array with size 4) and secondly a class which is a HashMap, and has an Array of Arrays as a member (here represented by class Bar).

Both classes implement begin() and end() so you can use a foreach loop to iterate over all elements.

The iterator type of Array is simply T* and const T* for the const variant. For Bar there is a special class Iterator, which correctly iterates through all members of the Array<Array<T>>. You should now have a look at the classes I provided, so you know what exactly I'm talking about.

But now there is a problem when I call bar.begin() on a const Bar object


The Iterator class determines the ArrayIterator and ElementIterator types automatically (via decltype of T::begin()), because in my real application almost everything is templated and that's why I don't know the exact type in advance.

I figured out the problem is that decltype(((T*)nullptr)->begin()) always chooses the non-const begin() function of T, which absolutely makes sense since I haven't written (const T*)nullptr.

If I now call it from a const context, it will fail to assign a const T* like data.last().end() to the internal T* from the decltype which actually should be a const T*.

I can workaround the problem by declaring a second class ConstIterator which does everything exactly like the non-const one, but uses (const Array<T>*)nullptr and (const T*)nullptr inside the decltype statements.

So what can I do without copying the whole Bar::Iterator class?


Simplified code:

template<class T>
class Array
{
    T data[4];

    T last() { return data[3]; }


    T* begin()             { return data; };
    T* end()               { return data + 4; };

    const T* begin() const { return data; };
    const T* end() const   { return data + 4; };
}

template<class T>
class Bar
{
    class Iterator
    {
        using ArrayIterator = decltype(((Array<T>*)nullptr)->begin());
        using ElementIterator = decltype(((T*)nullptr)->begin());

        Iterator(const ArrayIterator& beg, const ArrayIterator& end)
        {
            //initialize the iterator to the first element of the first array
            //(and rembember end)
        };

        Iterator(const ElementIterator& cur)
        {
            //initialize the iterator to the current element
        };

        //++ will iterate go to next element and eventually jump to the next array.
        //== returns true if the current element is the same
    };

    Array<T> data;

    Iterator begin()       { return Iterator(data.begin(), data.end()); };
    Iterator end()         { return Iterator(data.last().end()); };

    Iterator begin() const { return Iterator(data.begin(), data.end()); };
    Iterator end() const   { return Iterator(data.last().end()); };
};

Aucun commentaire:

Enregistrer un commentaire