Reputation: 8431
I have a class which has a private collection of std::shared_ptr
s, like:
class Foo
{
private:
using Bars = std::vector<std::shared_ptr<Bar>>;
Bars items_;
}
Given an instance of Foo
, I want to be able to iterate over the Bar
objects in items_
directly - hiding that the collection actually contains pointers. I believe the only thing that needs to change from Bars::const_iterator
is operator*
, is it ok to just derive from it and implement operator*
? i.e.
class Iterator : public Bars::const_iterator
{
public:
Iterator(Bars::const_iterator it) : Bars::const_iterator {it} {}
const string& operator*() const
{
return *Bars::const_iterator::operator*();
}
};
And then provide begin
and end
methods for Foo
:
Foo::Iterator Foo::begin() const noexcept { return Iterator {std::cbegin(items_)}; }
Foo::Iterator Foo::end() const noexcept { return Iterator {std::cend(items_)}; }
Upvotes: 1
Views: 88
Reputation: 76235
While most types in the standard library are not designed to be derived from, in this case it should be okay. The two dangers of inheritance are slicing and non-virtual destruction; for iterators these just aren't going to happen. No slicing because iterators are passed as template arguments, so the exact type is always used, and no non-virtual destruction because nobody in their right mind will create copies of iterators on the free store and delete them through a pointer to the base type (assuming they can figure out what it is).
EDIT: as Dieter Lücking points out, you'll also need to provide a typedef for iterator_type
that matches your type:
typedef Iterator iterator_type;
EDIT: as Dieter Lücking points out, this one alone is not sufficient. You're providing an operator*
, and need to provide all the typedefs that refer to the return type of that operator.
Upvotes: 3
Reputation:
For flexibility you may just write an adapter:
#include <type_traits>
template <typename Iterator>
class random_access_pointer_iterator
{
// Types
// =====
public:
typedef Iterator iterator_type;
typedef std::random_access_iterator_tag iterator_category;
using difference_type = typename iterator_type::difference_type;
using pointer = decltype(&**std::declval<iterator_type>());
using value_type = typename std::remove_pointer<pointer>::type;
typedef value_type& reference;
// Construction
// ============
public:
explicit random_access_pointer_iterator(iterator_type iterator)
: m_iterator(iterator)
{}
// Element Access
// ==============
public:
const iterator_type& base() const { return m_iterator; }
iterator_type& base() { return m_iterator; }
operator iterator_type () const { return m_iterator; }
// Iterator
// ========
public:
reference operator * () const { return **m_iterator; }
pointer operator -> () const { return &(**m_iterator); }
random_access_pointer_iterator& operator ++ () {
++m_iterator;
return *this;
}
random_access_pointer_iterator operator ++ (int) {
random_access_pointer_iterator tmp(*this);
++m_iterator;
return tmp;
}
random_access_pointer_iterator& operator += (difference_type n) {
m_iterator += n;
return *this;
}
random_access_pointer_iterator& operator -- () {
--m_iterator;
return *this;
}
random_access_pointer_iterator operator -- (int) {
random_access_pointer_iterator tmp(*this);
--m_iterator;
return tmp;
}
random_access_pointer_iterator& operator -= (difference_type n) {
m_iterator -= n;
return *this;
}
private:
iterator_type m_iterator;
};
template <typename Iterator>
inline random_access_pointer_iterator<Iterator> operator + (
random_access_pointer_iterator<Iterator> i,
typename random_access_pointer_iterator<Iterator>::difference_type n) {
return i += n;
}
template <typename Iterator>
inline random_access_pointer_iterator<Iterator> operator - (
random_access_pointer_iterator<Iterator> i,
typename random_access_pointer_iterator<Iterator>::difference_type n) {
return i -= n;
}
template <typename Iterator>
inline typename random_access_pointer_iterator<Iterator>::difference_type
operator - (
const random_access_pointer_iterator<Iterator>& a,
const random_access_pointer_iterator<Iterator>& b) {
return a.base() - b.base();
}
template <typename Iterator>
inline bool operator == (
const random_access_pointer_iterator<Iterator>& a,
const random_access_pointer_iterator<Iterator>& b) {
return a.base() == b.base();
}
template <typename Iterator>
inline bool operator != (
const random_access_pointer_iterator<Iterator>& a,
const random_access_pointer_iterator<Iterator>& b) {
return a.base() != b.base();
}
template <typename Iterator>
inline bool operator < (
const random_access_pointer_iterator<Iterator>& a,
const random_access_pointer_iterator<Iterator>& b) {
return a.base() < b.base();
}
template <typename Iterator>
inline bool operator <= (
const random_access_pointer_iterator<Iterator>& a,
const random_access_pointer_iterator<Iterator>& b) {
return a.base() <= b.base();
}
template <typename Iterator>
inline bool operator > (
const random_access_pointer_iterator<Iterator>& a,
const random_access_pointer_iterator<Iterator>& b) {
return a.base() > b.base();
}
template <typename Iterator>
inline bool operator >= (
const random_access_pointer_iterator<Iterator>& a,
const random_access_pointer_iterator<Iterator>& b) {
return a.base() >= b.base();
}
#include <cassert>
#include <memory>
#include <vector>
int main() {
using vector = std::vector<std::shared_ptr<int>>;
auto p = std::make_shared<int>(0);
vector v = { p };
using iterator = random_access_pointer_iterator<vector::iterator>;
iterator a(v.begin());
iterator b(v.end());
assert(*a == 0);
assert(a.operator -> () == &*p);
++a;
assert(a == b);
--a;
assert(a != b);
assert(a++ != b);
assert(a-- == b);
assert(a + 1 == b);
assert(a == b - 1);
assert(b - a == 1);
assert(a < b);
assert(a <= b);
assert(b > a);
assert(b >= a);
}
Having this, you can use any random access iterator (vector, deque, ... ) and use any pointer type (raw pointer, shared_ptr, ...)
Note: In your case - when you derive from the iterator of the vector you will have to adjust the type definitions, too.
Note: I do not like 'random_access_pointer_iterator', but I have nothing better on my mind.
Upvotes: 3