vendredi 4 mai 2018

const, span, and iterator trouble

I try to write an iterator that iterates over a container by index. A It and a const It both allow changing the content of the container. A Const_it and a const Const_it both forbid changing the content of the container.

After that, I try to write a span<T> over a container. For a type T that is not const, both const span<T> and span<T> allows changing the content of the container. Both const span<const T> and span<const T> forbid changing the content of the container.

The code does not compile because:

    // *this is const within a const method
    // But It<self_type> requires a non-const *this here.
    // So the code does not compile
    It<self_type> begin() const { return It<self_type>(*this, 0); }

If I make the constructor of It to accept a const container, it doesn't look right because the iterator can modify the content of the container.

If I get rid of the const of the method, then for a non-const type T, a const span<T> cannot modify the container.

It inherits from Const_it to allow implicit conversion from It to Const_it during template instantiation.

I use a pointer instead of a reference to in the iterators (const C* container_;) for allowing assigning one iterator to another iterator.

I suspect something is very wrong here because I even think about:

Does cast away const of *this cause undefined behavior?

But I don't know how to fix it.

Test:

#include <vector>
#include <numeric>
#include <iostream>

template<typename C>
class Const_it {
    typedef Const_it<C> self_type;
public:
    Const_it(const C& container, const int ix)
            : container_(&container), ix_(ix) {}
    self_type& operator++() {
        ++ix_;
        return *this;
    }

    const int& operator*() const {
        return ref_a()[ix_];
    }

    bool operator!=(const self_type& rhs) const {
        return ix_ != rhs.ix_;
    }

protected:
    const C& ref_a() const { return *container_; }
    const C* container_;
    int ix_;
};

template<typename C>
class It : public Const_it<C> {
    typedef Const_it<C> Base;
    typedef It<C> self_type;
public:
    //It(const C& container.
    It(C& container, const int ix)
            : Base::Const_it(container, ix) {}
    self_type& operator++() {
        ++ix_;
        return *this;
    }

    int& operator*() const {
        return mutable_a()[ix_];
    }

private:
    C& mutable_a() const { return const_cast<C&>(ref_a()); }
    using Base::ref_a;
    using Base::container_;
    using Base::ix_;
};


template <typename V>
class span {
    typedef span<V> self_type;
public:
    explicit span(V& v) : v_(v) {}
    It<self_type> begin() { return It<self_type>(*this, 0); }
    // *this is const within a const method
    // But It<self_type> requires a non-const *this here.
    // So the code does not compile
    It<self_type> begin() const { return It<self_type>(*this, 0); }
    It<self_type> end() { return It<self_type>(*this, v_.size()); }
    It<self_type> end() const { return It<self_type>(*this, v_.size()); }

    int& operator[](const int ix) {return v_[ix];}
    const int& operator[](const int ix) const {return v_[ix];}
private:
    V& v_;
};


int main() {
    typedef std::vector<int> V;
    V v(10);
    std::iota(v.begin(), v.end(), 0);
    std::cout << v.size() << "\n";
    const span<V> s(v);
    for (auto&& x : s) {
        x = 4;
        std::cout << x << "\n";
    }
}

Aucun commentaire:

Enregistrer un commentaire