2016-02-04 2 views
1

у меня есть класс, который имеет частную коллекцию std::shared_ptr с, как:Выведение из стандартных контейнеров итераторов

class Foo 
{ 
private: 
    using Bars = std::vector<std::shared_ptr<Bar>>; 
    Bars items_; 
} 

Учитывая экземпляр Foo, я хочу, чтобы иметь возможность перебирать Bar объектов в items_ непосредственно - скрывать, что коллекция фактически содержит указатели. Я считаю, что единственное, что нужно изменить с Bars::const_iterator, это operator*, это нормально, чтобы просто извлечь из него и реализовать operator*? т.е.

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*(); 
    } 
}; 

А затем обеспечить begin и end методы Foo:

Foo::Iterator Foo::begin() const noexcept { return Iterator {std::cbegin(items_)}; } 
Foo::Iterator Foo::end() const noexcept { return Iterator {std::cend(items_)}; } 
+0

Вы можете безопасно получить практически из любого класса при условии, что наследование является приватным. Вам понадобится декларация 'using' для повторного экспорта членов, которые вы хотите получить. –

ответ

3

Хотя большинство типов в стандартной библиотеке, не предназначены для быть получены из, в этом случае оно должно быть в порядке. Двумя опасностями наследования являются нарезка и не виртуальное разрушение; для итераторов это просто не произойдет. Нет разрезания, потому что итераторы передаются как аргументы шаблона, поэтому точный тип всегда используется и не виртуальное уничтожение, потому что никто в их собственном уме не создаст копии итераторов в свободном хранилище и не удалит их с помощью указателя на базовый тип (предполагая, что они могут понять, что это такое).

EDIT: а Дитер Lücking указывает, вам также необходимо предоставить ЬурейеЕ для iterator_type, который соответствует вашему типу:

typedef Iterator iterator_type; 

EDIT: а Дитер Lücking указывает, только этот один не является достаточным. Вы предоставляете operator* и должны предоставить все typedefs, которые ссылаются на тип возврата этого оператора.

+0

Нет, не то, что typedef, вам нужен указатель, value_type и reference –

+0

@ DieterLücking - это не те, что унаследованы от базового класса? –

+0

Нет, value_type является shared_ptr , но итератор возвращает T –

3

Для гибкости вы можете просто написать адаптер:

#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); 
} 

Имея это, вы можете использовать любой итератор произвольного доступа (вектор, Deque, ...) и использовать любой тип указателя (сырой указатель, shared_ptr, ...)

Примечание: в вашем случае - при выводе из итератора вектора вам также придется корректировать определения типов.

Примечание: Мне не нравится «random_access_pointer_iterator», но у меня нет ничего лучше на уме.

Смежные вопросы