c++iteratorrange-based-loop

how to write forward iterator using private std::vector base class


I need a vector class that exposes a small subset of the std::vector API. Everything works except range-based for. Here my attempt at implementing a forward iterator, which however does not compile.

#include <vector>
#include <iostream>

template <class T>
class OwningVector : private std::vector<T*> {
    using super = std::vector<T*>;

public:
    OwningVector() = default;
    ~OwningVector()
    {
        for (T* e : *this)
            delete e;
        super::clear();
    }
    OwningVector(const OwningVector& other)
        : super()
    {
        super::reserve(other.size());
        for (T* e : other)
            super::emplace_back(e->clone());
    }
    OwningVector& operator=(const OwningVector& other)
    {
        if (this == &other)
            return *this;
        OwningVector ret(other);
        swap(*this, ret);
        return *this;
    }

    void emplace_back(T* e) { super::emplace_back(e); }

    size_t size() const { return super::size(); }
    T* const& operator[](int i) const { return super::operator[](i); }
    T* const& at(int i) const { return super::at(i); }
    const T* back() const { return super::back(); }

    // Here the questionable part of my code:

    class Iterator : public std::vector<T*>::iterator {};

    Iterator begin() const { return super::begin(); }
    Iterator end() const { return super::end(); }
    Iterator begin() { return super::begin(); }
    Iterator end() { return super::end(); }
};

class A {
public:
    A(int m) : m(m) {}
    int m;
};

int main() {
    OwningVector<A> v;
    v.emplace_back(new A(1));
    for (const A*const a: v)
        std::cout << a->m << std::endl;
}

Compilation fails:

h.cpp: In instantiation of ‘OwningVector<T>::Iterator OwningVector<T>::begin() [with T = A]’:
h.cpp:56:27:   required from here
h.cpp:43:43: error: could not convert ‘((OwningVector<A>*)this)->OwningVector<A>::<anonymous>.std::vector<A*, std::allocator<A*> >::begin()’ from ‘std::vector<A*, std::allocator<A*> >::iterator’ to ‘OwningVector<A>::Iterator’
   43 |     Iterator begin() { return super::begin(); }
      |                               ~~~~~~~~~~~~^~
      |                                           |
      |                                           std::vector<A*, std::allocator<A*> >::iterator
h.cpp: In instantiation of ‘OwningVector<T>::Iterator OwningVector<T>::end() [with T = A]’:
h.cpp:56:27:   required from here
h.cpp:44:39: error: could not convert ‘((OwningVector<A>*)this)->OwningVector<A>::<anonymous>.std::vector<A*, std::allocator<A*> >::end()’ from ‘std::vector<A*, std::allocator<A*> >::iterator’ to ‘OwningVector<A>::Iterator’
   44 |     Iterator end() { return super::end(); }
      |                             ~~~~~~~~~~^~
      |                                       |
      |                                       std::vector<A*, std::allocator<A*> >::iterator

Solution

  • class Iterator : public std::vector<T*>::iterator {};
    

    Why? This looks like a certain other language's way of doing that. It's also not guaranteed to work because vector<T*>::iterator may actually be a pointer type, in which case you can't derive from it.

    using Iterator = typename super::iterator;
    using ConstIterator = typename super::const_iterator;
    
    ConstIterator begin() const { return super::begin(); }
    ConstIterator end() const { return super::end(); }
    Iterator begin() { return super::begin(); }
    Iterator end() { return super::end(); }
    

    This works. The typename is required up to C++17, can be dropped for C++20.