2013-10-01 3 views
4

Прямо сейчас у меня есть класс, который может удовлетворить требованиям API с помощью итератора с произвольным доступом. Однако я могу представить себе ситуацию, когда реализация изменится, и может быть предоставлен только передовой итератор.Могу ли я легко переопределить категорию (it) итератора (STL)?

Поэтому я хотел бы ограничить абонентов от использования функции случайного доступа. Я знаю, что могу написать свою собственную реализацию (например, limited_bar_iterator), но задавался вопросом, есть ли что-нибудь более простое (т. Е. Требует меньше кодирования).

class BAR { ... }; 

class FOO { 
public: 
    // Bad...clients may expect 'bar_iterator' to be random access... 
    typedef std::vector<BAR>::iterator bar_iterator; 

    bar_iterator begin_bar() const; 
    bar_iterator end_bar() const; 

    // Possible solution here! 
    class restricted_bar_iterator : 
     public std::iterator< std::forward_iterator_tag, BAR > { ... }; 
}; 


void baz() 
{ 
    FOO foo; 
    bar_iterator it = foo.begin_bar() + 5; // want a compile time error here! 
} 
+0

Я хотел бы попробовать труднее всего избежать этой ситуации, но я * действительно * хотел сделать это, я бы на общественном наследованию 'вектора :: [const_] iterator' и явная специализация' станд :: iterator_traits', потому что это то, что занимает наименьшее количество кода. Я не стану считать это ответом, потому что это действительно предпочтительнее, чтобы избежать этого полностью. –

+0

Наследовать от 'std :: iterator ' и определить все необходимые вам методы, чтобы он вел себя так, как вы ожидали. – andre

+0

@AlexandreC., Даже если 'вектор :: iterator' является типом класса, вам все равно нужно заменить, по крайней мере' оператор ++ два 'перегрузки, в противном случае они возвращают базовый тип, и только специализирующимся' iterator_traits' не останавливаются выше от попыток использовать операции RandomAccessIterator код, даже если категория говорит ForwardIterator –

ответ

4

Вот пример использования Boost Iterator Adaptor. Я использовал int вместо BAR.

#include <boost/iterator/iterator_adaptor.hpp> 
#include <vector> 

struct iterator : 
    public boost::iterator_adaptor< 
     iterator,     // the name of our class, see docs for details 
     std::vector<int>::iterator, // underlying base iterator 
     boost::use_default,   // for value type 
     boost::forward_traversal_tag // all the boilerplate for this! 
    > 
{ 
    // need this to convert from vector::iterator to ours 
    explicit iterator(std::vector<int>::iterator i) 
     : iterator::iterator_adaptor_(i) {} 
}; 

int main() 
{ 
    std::vector<int> v; 
    iterator it(v.begin()); 
    ++it; // OK 
    it += 1; // ERROR 
} 

Это эффективно использует std::vector<T>::iterator в качестве базового класса, но только позволяет операции, которые определены для передних итераторов. Недостатком являются сообщения об ошибках - они не очень красивы.

3

Вы определенно должны сделать некоторые кодирования, но вы могли бы наследовать от базового типа, чтобы получить большую часть функциональности, просто перекрывая операции, которые не хотят работать, либо путем определения их как удаленные в C++ 11 или сделать их частными и нереализованными в 03 C++:

class FOO { 
    // Bad...clients may expect 'bar_iterator' to be random access... 
    typedef std::vector<BAR>::iterator bar_iterator_impl; 

public: 
    // Possible solution here! 
    struct bar_iterator : bar_iterator_impl { 
     bar_iterator& operator++() { 
     ++static_cast<bar_iterator_impl&>(*this); 
     return *this; 
     } 
     bar_iterator operator++(int) { 
     bar_iterator copy(*this); 
     ++*this; 
     return copy; 
     } 

     typedef std::forward_iterator_tag iterator_category;  
     typedef std::iterator_traits<bar_iterator_impl>::value_type value_type; 
     typedef std::iterator_traits<bar_iterator_impl>::difference_type difference_type; 
     typedef std::iterator_traits<bar_iterator_impl>::pointer pointer; 
     typedef std::iterator_traits<bar_iterator_impl>::reference reference; 

    private: 
     friend void operator+(bar_iterator const&, long); 
     friend void operator+(long, bar_iterator const&); 
     friend void operator-(bar_iterator const&, long); 
     friend void operator-(long, bar_iterator const&); 
    }; 

    bar_iterator begin_bar() const; 
    bar_iterator end_bar() const; 
}; 

Однако это работает только если std::vector<BAR>::iterator является тип класса, и он может быть указателем, в этом случае она не может быть полученным. Чтобы быть переносимым, вам нужно будет определить весь API-интерфейс итератора самостоятельно.

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