4

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
Joachim W
  • 7,290
  • 5
  • 31
  • 59

1 Answers1

5
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.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • Works, but does not solve my problem. I need private inheritance to hide some methods of the base class. – Joachim W May 15 '22 at 11:40
  • By the way, if you change the inheritance to `public`, then you can drop the entire iterator redefinition for good. – Joachim W May 15 '22 at 11:40
  • @JoachimW re "to hide some methods of the base class" the iterator has no methods. It can simply be an alias for `T**` in this case (depending on the implementation). See the full code here https://gcc.godbolt.org/z/rb7Yx3Er7 – Aykhan Hagverdili May 15 '22 at 11:41
  • Here the reason why public inheritance does not work for me: https://stackoverflow.com/questions/72247990/ – Joachim W May 15 '22 at 11:51
  • 1
    @JoachimW I am not suggesting you change the private inheritance. I am suggesting you add these typedefs into your class. Please see the link in my previous comment. Please review the full code there. – Aykhan Hagverdili May 15 '22 at 11:53
  • Sorry, I misread your answer. So no change to line 5. That's good. However, after changing the iterator section, it still does not compile. `h.cpp:39:22: error: need ‘typename’ before ‘OwningVector::super::iterator’ because ‘OwningVector::super’ is a dependent scope 39 | using Iterator = super::iterator;` – Joachim W May 15 '22 at 12:01
  • @JoachimW It compiles fine here https://gcc.godbolt.org/z/qrnnhxYac – Aykhan Hagverdili May 15 '22 at 12:02
  • It compiles under C++20, but not C++17. Under C++11 to C++17, I need to insert `typename` after the equal signs. – Joachim W May 15 '22 at 12:09
  • 1
    With that, we do have a working solution. Thank you very much, @Ayxan. – Joachim W May 15 '22 at 12:10