Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Iterator Facade

Boost , ,

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Iterator Facade

Author: Дэвид Абрахамс, Джереми Сик, Томас Витт
Contact: В то время как интерфейс итератора богат, есть базовое подмножество интерфейса, которое необходимо для всей функциональности. Мы определили следующие основные модели поведения итераторов:

  • отсрочка
  • возрастание
  • декремент
  • сравнение равенства
  • движение случайного доступа
  • измерение расстояния

В дополнение к поведению, перечисленному выше, основные элементы интерфейса включают связанные типы, подверженные через черты итератора:значение_тип,ссылка,Различие_типиитератор_категория.

Фасад итератора использует Curiously Recurring Template Pattern (CRTP)[Cop95], чтобы пользователь мог указать поведениеiterator_facadeв производном классе. Бывшие проекты использовали объекты политики для определения поведения, но этот подход был отброшен по нескольким причинам:

  1. Создание и возможное копирование объекта политики может создать накладные расходы, которых можно избежать при нынешнем подходе.
  2. Подход «объект политики» не позволяет создавать пользовательские конструкторы на созданных типах итераторов, что является существенной особенностью, еслиiterator_facadeследует использовать в других вариантах осуществления библиотеки.
  3. Без использования CRTP стандартное требование к оператору итератора++возвращает сам тип итератора, что означает, что все итераторы, построенные с библиотекой, должны быть специализациейiterator_facade<...>, а не что-то более описательное, какindirect_iterator. Для создания новых параметризированных итераторов и отдельного итераторапотребуются громоздкие метафункции генератора типа.Это невозможно.

Usage

Пользовательiterator_facadeполучает свой класс итератора из специализацииiterator_facadeи проходит класс производного итератора какiterator_facadeпервый параметр шаблона. Порядок других параметров шаблона был тщательно выбран, чтобы воспользоваться полезными по умолчанию. Например, при определении постоянного итератора lvalue пользователь может передать конст-квалифицированную версию параметраvalue_typeитератораiterator_facade'sValueи опустить параметр.Ссылкапараметр, который следует.

Полученный класс итератора должен определять функции члена, реализующие основные модели поведения итератора. В следующей таблице описаны выражения, которые должны быть действительными в зависимости от категории производного типа итератора. Эти функции элементов кратко описаны ниже и более подробно в требованиях к фасаду итератора.

Expression Effects
i.dereference() Доступ к значению, указанному
i.equal(j) Сравните для равенства сj
i.increment() Продвижение на одну позицию
i.decrement() Отступление на одну позицию
i.advance(n) nположения
i.distance_to(j) Измерить расстояние доj

В дополнение к реализации основных функций интерфейса итератор, полученный изiterator_facade, обычно определяет несколько конструкторов. Чтобы смоделировать любую из стандартных концепций итератора, итератор должен иметь конструктор копий. Кроме того, если тип итератораXпредназначен для автоматического взаимодействия с другим типом итератораY(как с постоянными и изменяемыми итераторами), то должно быть неявное преобразование изXвYили изYвX(но не оба), обычно реализованное в качестве конструктора преобразования. Наконец, если итератор предназначен для моделирования итератора переднего поворота или более усовершенствованной концепции итератора, требуется конструктор по умолчанию.

Iterator Core Access

iterator_facadeи реализации оператора должны иметь возможность доступа к основным функциям-членам в производном классе. Публикация основных функций-членов раскрывает пользователю детали реализации. Используемый здесь дизайн гарантирует, что детали реализации не отображаются в публичном интерфейсе производного типа итератора.

Предотвращение прямого доступа к основным функциям имеет два преимущества. Во-первых, у пользователя нет возможности случайно использовать функцию члена итератора, когда был предназначен элемент типа значения. В прошлом это было проблемой с интеллектуальными реализациями указателей. Второе и главное преимущество заключается в том, что реализаторы библиотеки могут свободно обмениваться ручной реализацией итератора на реализацию на основеiterator_facade, не опасаясь взлома кода, который имел доступ к функциям публичного ядра.

В наивной реализации сохранение в тайне функций основного члена производного класса потребовало бы от него предоставления дружбыiterator_facadeи каждому из семи операторов. Для того чтобы уменьшить бремя ограничения доступа, предоставляетсяiterator_core_access, класс, который действует как шлюз к функциям основного члена в классе производного итератора. Автору производного класса нужно только предоставить дружбуiterator_core_access, чтобы его основные функции члена были доступны библиотеке.

iterator_core_accessбудет, как правило, реализован в виде пустого класса, содержащего только частные статические функции члена, которые вызывают функции основного члена итератора. Однако нет необходимости стандартизировать протокол шлюза. Обратите внимание, что даже еслиитератор_core_accessиспользует публичные функции-члены, это не откроет лазейку безопасности, поскольку каждая функция-ядро сохраняет инварианты итератора.

operator[]

Оператор индексации для обобщенного итератора представляет особые проблемы. Оператор итератора случайного доступадолжен только вернуть что-то конвертируемое в егозначение_тип. Требование, чтобы он возвращал значение l, исключало бы в настоящее время законные итераторы случайного доступа, которые содержат указанное значение в элементе данных (например,подсчёт_iterator), потому что* (p+n)является ссылкой на временный итераторp+n, который разрушается, когдаоператор []возвращается.

Письменные итераторы, построенные с помощьюiterator_facade, реализуют семантику, требуемую предпочтительным разрешениемвыпуска 299и принятую предложениемn1550: результатp[n]является объектом, конвертируемым в значение итератора[типа], иp[n]xэквивалентен* [p+n]=x(Примечание: Этот объект результата может быть реализован в качестве прокси, содержащего копиюp+n. Этот подход будет работать должным образом для любого итератора случайного доступа независимо от других деталей его реализации. Пользователь, который знает больше о реализации своего итератора, может свободно реализовать оператора, который возвращает значение l в классе производного итератора; он будет скрывать тот, который поставляетсяiterator_facadeот клиентов своего итератора.

operator->

Тип ссылкичитаемого итератора (и сегодняшнего итератора ввода) на самом деле не должен быть ссылкой, если он конвертируем в значениеитератора. Однако, когдазначение_типявляется классом, все равно должна быть возможность доступа к членам черезоператора->. Поэтому итератор, чейэталонныйтип фактически не является эталонным, должен возвращать прокси-сервер, содержащий копию указанной величины от егооператора->.

Типы возврата дляитератора_фасада'sоператора->иоператора[]явно не указаны. Вместо этого эти типы описаны с точки зрения набора требований, которые должны быть удовлетворены реализациейiterator_facade.

[Cop95][1,2][Coplien, 1995] Coplien, J., Curiously Recurring Template Patterns, C++ Report, February 1995, pp.

Reference

template <
    class Derived
  , class Value
  , class CategoryOrTraversal
  , class Reference  = Value&
  , class Difference = ptrdiff_t
>
class iterator_facade {
 public:
    typedef remove_const<Value>::type value_type;
    typedef Reference reference;
    typedef Value* pointer;
    typedef Difference difference_type;
    typedef /* see below */ iterator_category;
    reference operator*() const;
    /* see below */ operator->() const;
    /* see below */ operator[](difference_type n) const;
    Derived& operator++();
    Derived operator++(int);
    Derived& operator--();
    Derived operator--(int);
    Derived& operator+=(difference_type n);
    Derived& operator-=(difference_type n);
    Derived operator-(difference_type n) const;
 protected:
    typedef iterator_facade iterator_facade_;
};
// Comparison operators
template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type // exposition
operator ==(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator !=(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator <(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
           iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator <=(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator >(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
           iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator >=(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
// Iterator difference
template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
/* see below */
operator-(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
          iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
// Iterator addition
template <class Dr, class V, class TC, class R, class D>
Derived operator+ (iterator_facade<Dr,V,TC,R,D> const&,
                   typename Derived::difference_type n);
template <class Dr, class V, class TC, class R, class D>
Derived operator+ (typename Derived::difference_type n,
                   iterator_facade<Dr,V,TC,R,D> const&);

The iterator_category member of iterator_facade is

iterator-category(CategoryOrTraversal, value_type, reference)

гдеитератор-категорияопределена следующим образом:

iterator-category(C,R,V) :=
   if (C is convertible to std::input_iterator_tag
       || C is convertible to std::output_iterator_tag
   )
       return C
   else if (C is not convertible to incrementable_traversal_tag)
       the program is ill-formed
   else return a type X satisfying the following two constraints:
      1. X is convertible to X1, and not to any more-derived
         type, where X1 is defined by:
           if (R is a reference type
               && C is convertible to forward_traversal_tag)
           {
               if (C is convertible to random_access_traversal_tag)
                   X1 = random_access_iterator_tag
               else if (C is convertible to bidirectional_traversal_tag)
                   X1 = bidirectional_iterator_tag
               else
                   X1 = forward_iterator_tag
           }
           else
           {
               if (C is convertible to single_pass_traversal_tag
                   && R is convertible to V)
                   X1 = input_iterator_tag
               else
                   X1 = C
           }
      2. category-to-traversal(X) is convertible to the most
         derived traversal tag type to which X is also
         convertible, and not to any more-derived traversal tag
         type.

[Примечание: намерение состоит в том, чтобы позволитьiterator_categoryбыть одним из пяти исходных тегов категории, когда конвертируемость в один из тегов обхода не добавит информации]

Шаблонenable_if_interoperable, используемый выше, предназначен для экспозиции. Операторы-члены должны находиться только в наборе перегрузок при условии, что производные типыDr1иDr2совместимы, что означает, что по меньшей мере один из типов является конвертируемым в другой. Подходпозволяет_if_interoperableиспользовать SFINAE для вывода операторов из набора перегрузок, когда типы не совместимы. Операторы должны вести себякак-есливключить_if_интероперабельностьбыло определено, что:

template <bool, typename> enable_if_interoperable_impl
{};
template <typename T> enable_if_interoperable_impl<true,T>
{ typedef T type; };
template<typename Dr1, typename Dr2, typename T>
struct enable_if_interoperable
  : enable_if_interoperable_impl<
        is_convertible<Dr1,Dr2>::value || is_convertible<Dr2,Dr1>::value
      , T
    >
{};

iterator_facade Requirements

В следующей таблице описаны типичные действительные выражения поiterator_facade'sПроизводныйпараметр, в зависимости от концепции(ей) итератора, которую он будет моделировать. Операции в первой колонке должны быть доступны для членских функций классаiterator_core_access. Кроме того,static_cast(iterator_facade*)должны быть хорошо сформированы.

В приведенной ниже таблицеFявляетсяитератором_фасада,aявляется объектом типаX,bиcconstX,nявляется объектомF:::difference_type,yyявляется постоянным объектом однопропускного итератора типа, взаимодействующего с [

iterator_facade Core Operations

Expression Return Type Assertion/Note Used to implement Iterator Concept(s)
c.dereference() F:: ссылка   Читаемый итератор, письменный итератор
c.equal(y) конвертируемый в bool cиyотносятся к одной и той же позиции. Итератор Single Pass
a.increment() неиспользованный   Необычный итератор
a.decrement() неиспользованный   Двунаправленный итератор поворотов
a.advance(n) неиспользованный   Случайный доступ Traversal Iterator
c.distance_to(z) конвертируемый вF::difference_type расстояние (c,X(z)]. Случайный доступ Traversal Iterator

iterator_facade operations

Операции в этом разделе описаны в терминах операций на базовом интерфейсеПроизводный, который может быть недоступным (т.е. закрытым). Реализация должна обеспечивать доступ к этим операциям через функции-члены классаiterator_core_access.

ссылкаоператор*()const;

Returns:static_cast<Derived const*>(this)->dereference()

оператор->()const;(см.ниже)

Returns:

If reference is a reference type, an object of type pointer equal to:

&static_cast<Derived const*>(this)->dereference()

Otherwise returns an object of unspecified type such that, (*static_cast<Derived const*>(this))->m is equivalent to (w = **static_cast<Derived const*>(this), w.m) for some temporary object w of type value_type.

unspecified operator[](difference_type n) const;

Returns:an object convertible to value_type. For constant objects v of type value_type, and n of type difference_type, (*this)[n] = v is equivalent to *(*this + n) = v, and static_cast<value_type const&>((*this)[n]) is equivalent to static_cast<value_type const&>(*(*this + n))

Derived&operator++();

Effects:
static_cast<Derived*>(this)->increment();
return *static_cast<Derived*>(this);

Выводитсяоператор++(int);

Effects:
Derived tmp(static_cast<Derived const*>(this));
++*this;
return tmp;

Производный &оператор--();

Effects:
static_cast<Derived*>(this)->decrement();
return *static_cast<Derived*>(this);

Производныйоператор - (int);

Effects:
Derived tmp(static_cast<Derived const*>(this));
--*this;
return tmp;

Производный &оператор +=(разница_типn);

Effects:
static_cast<Derived*>(this)->advance(n);
return *static_cast<Derived*>(this);

Derived&operator-=(difference_typen);

Effects:
static_cast<Derived*>(this)->advance(-n);
return *static_cast<Derived*>(this);

Выводитсяоператор-(различие_типn)const;

Effects:
Derived tmp(static_cast<Derived const*>(this));
return tmp -= n;
template <class Dr, class V, class TC, class R, class D>
Derived operator+ (iterator_facade<Dr,V,TC,R,D> const&,
                   typename Derived::difference_type n);
template <class Dr, class V, class TC, class R, class D>
Derived operator+ (typename Derived::difference_type n,
                   iterator_facade<Dr,V,TC,R,D> const&);
Effects:
Derived tmp(static_cast<Derived const*>(this));
return tmp += n;
template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator ==(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Returns:

if is_convertible<Dr2,Dr1>::value

then

((Dr1 const&)lhs).equal((Dr2 const&)rhs).

Otherwise,

((Dr2 const&)rhs).equal((Dr1 const&)lhs).

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator !=(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Returns:

if is_convertible<Dr2,Dr1>::value

then

!((Dr1 const&)lhs).equal((Dr2 const&)rhs).

Otherwise,

!((Dr2 const&)rhs).equal((Dr1 const&)lhs).

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator <(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
           iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Returns:

if is_convertible<Dr2,Dr1>::value

then

((Dr1 const&)lhs).distance_to((Dr2 const&)rhs) < 0.

Otherwise,

((Dr2 const&)rhs).distance_to((Dr1 const&)lhs) > 0.

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator <=(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Returns:

if is_convertible<Dr2,Dr1>::value

then

((Dr1 const&)lhs).distance_to((Dr2 const&)rhs) <= 0.

Otherwise,

((Dr2 const&)rhs).distance_to((Dr1 const&)lhs) >= 0.

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator >(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
           iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Returns:

if is_convertible<Dr2,Dr1>::value

then

((Dr1 const&)lhs).distance_to((Dr2 const&)rhs) > 0.

Otherwise,

((Dr2 const&)rhs).distance_to((Dr1 const&)lhs) < 0.

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,bool>::type
operator >=(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
            iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Returns:

if is_convertible<Dr2,Dr1>::value

then

((Dr1 const&)lhs).distance_to((Dr2 const&)rhs) >= 0.

Otherwise,

((Dr2 const&)rhs).distance_to((Dr1 const&)lhs) <= 0.

template <class Dr1, class V1, class TC1, class R1, class D1,
          class Dr2, class V2, class TC2, class R2, class D2>
typename enable_if_interoperable<Dr1,Dr2,difference>::type
operator -(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs,
           iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
Return Type:

if is_convertible<Dr2,Dr1>::value

then

difference shall be iterator_traits<Dr1>::difference_type.

Otherwise

difference shall be iterator_traits<Dr2>::difference_type

Returns:

if is_convertible<Dr2,Dr1>::value

then

-((Dr1 const&)lhs).distance_to((Dr2 const&)rhs).

Otherwise,

((Dr2 const&)rhs).distance_to((Dr1 const&)lhs).

Tutorial Example

В этом разделе мы рассмотрим реализацию нескольких итераторов с использованиемiterator_facade, основанных на простом примере связанного списка полиморфных объектов. Этот пример был вдохновленпостингомКита Макдональда всписке рассылки Boost-Users.

The Problem

Скажем, мы написали полиморфный связанный список узлов базового класса:

# include <iostream>
struct node_base
{
    node_base() : m_next(0) {}
    // Each node manages all of its tail nodes
    virtual ~node_base() { delete m_next; }
    // Access the rest of the list
    node_base* next() const { return m_next; }
    // print to the stream
    virtual void print(std::ostream& s) const = 0;
    // double the value
    virtual void double_me() = 0;
    void append(node_base* p)
    {
        if (m_next)
            m_next->append(p);
        else
            m_next = p;
    }
 private:
    node_base* m_next;
};

Списки могут содержать объекты разных типов, связывая между собой специализации следующего шаблона:

template <class T>
struct node : node_base
{
    node(T x)
      : m_value(x)
    {}
    void print(std::ostream& s) const { s << this->m_value; }
    void double_me() { m_value += m_value; }
 private:
    T m_value;
};

И мы можем распечатать любой узел, используя следующего оператора потоковой передачи:

inline std::ostream& operator<<(std::ostream& s, node_base const& n)
{
    n.print(s);
    return s;
}

Наша первая задача - создать соответствующий итератор по этим спискам.

A Basic Iterator Using iterator_facade

Мы построим классnode_iterator, используя наследование изiterator_facadeдля реализации большинства операций итератора.

# include "node.hpp"
# include <boost/iterator/iterator_facade.hpp>
class node_iterator
  : public boost::iterator_facade<...>
{
   ...
};

Template Arguments for iterator_facade

iterator_facadeимеет несколько параметров шаблона, поэтому мы должны решить, какие типы использовать для аргументов. Параметры:Получено,Значение,КатегорияПутешественник,СсылкаиРазница.

Derived

Посколькуiterator_facadeпредназначен для использования с CRTP[Cop95], первым параметром является само название класса итератораnode_iterator.

Value

ПараметрЗначениеопределяетnode_iterator'sзначение_type. В этом случае мы повторяем надnode_baseобъектами, поэтомузначениебудетnode_base.

CategoryOrTraversal

Теперь мы должны определить, какаяконцепция обхода итераторанашаузел_итераторсобирается моделировать. Связанные с помощью Singly списки имеют только прямые ссылки, поэтому наш итератор не может бытьдвунаправленным итератором обхода. Наш итератор должен иметь возможность совершать несколько проходов по одному и тому же связанному списку (в отличие, скажем, отistream_iterator, который потребляет поток, по которому он проходит), поэтому он должен бытьпередним итератором обхода. Поэтому мы пройдембустер::forward_traversal_tagв этом положении1.

[1]iterator_facadeтакже поддерживает теги категорий старого стиля, поэтому мы могли бы пройтиstd::forward_iterator_tagздесь; в любом случае полученный итераторiterator_categoryв конечном итоге будетstd::forward_iterator_tag.

Reference

АргументReferenceстановится типом, возвращаемымnode_iterator's dereference operation, а также будет таким же, какstd::iterator_traits::reference. По умолчанию библиотека для этого параметра являетсяValue&; посколькуnode_base&является хорошим выбором для итератораэталонноготипа, мы можем опустить этот аргумент, или передатьиспользование_default.

Difference

АргументРазницаопределяет, как будет измеряться расстояние между двумяnode_iterators, а также будет таким же, какstd::iterator_traits::difference_type. По умолчанию библиотека дляРазницапредставляет собойstd::ptrdiff_t, подходящий тип для измерения расстояния между любыми двумя адресами в памяти, и тот, который работает практически для любого итератора, поэтому мы также можем опустить этот аргумент.

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

# include "node.hpp"
# include <boost/iterator/iterator_facade.hpp>
class node_iterator
  : public boost::iterator_facade<
        node_iterator
      , node_base
      , boost::forward_traversal_tag
    >
{
   ...
};

Constructors and Data Members

Далее нужно решить, как представлять позицию итератора. Это представление будет принимать форму членов данных, поэтому нам также нужно будет написать конструкторы для их инициализации. Положениеnode_iteratorвполне естественно представлено с помощью указателя наnode_base. Нам понадобится конструктор для создания итератора изnode_base*, и конструктор по умолчанию для удовлетворения требованийforward traversal iterator2.

# include "node.hpp"
# include <boost/iterator/iterator_facade.hpp>
class node_iterator
  : public boost::iterator_facade<
        node_iterator
      , node_base
      , boost::forward_traversal_tag
    >
{
 public:
    node_iterator()
      : m_node(0)
    {}
    explicit node_iterator(node_base* p)
      : m_node(p)
    {}
 private:
    ...
    node_base* m_node;
};
[2]Технически, в стандарте C++ практически нет требований к построенному по умолчанию итератору, поэтому, если бы мы действительно были обеспокоены эффективностью, мы могли бы написать конструктор по умолчанию, чтобы оставитьm_nodeнеинициализированным.

Implementing the Core Operations

Последним шагом является реализация основных операций, требуемых концепциями, которые мы хотим, чтобы наш итератор моделировал. Ссылаясь на таблицу, мы видим, что первые три строки применимы, потому чтоузел_итератордолжен удовлетворять требованиям кчитаемому итератору,одиночному итераторуиинкрементируемому итератору.

Поэтому мы должны предоставить,равныеиприращениячленов. Мы не хотим, чтобы эти участники стали частью публичного интерфейсаnode_iterator, поэтому мы можем сделать их частными и предоставить дружбу, чтобы повысить::iterator_core_access, "back-door", которыйiterator_facadeиспользует для получения доступа к основным операциям:

# include "node.hpp"
# include <boost/iterator/iterator_facade.hpp>
class node_iterator
  : public boost::iterator_facade<
        node_iterator
      , node_base
      , boost::forward_traversal_tag
    >
{
 public:
    node_iterator()
      : m_node(0) {}
    explicit node_iterator(node_base* p)
      : m_node(p) {}
 private:
    friend class boost::iterator_core_access;
    void increment() { m_node = m_node->next(); }
    bool equal(node_iterator const& other) const
    {
        return this->m_node == other.m_node;
    }
    node_base& dereference() const { return *m_node; }
    node_base* m_node;
};

Voilà; полный и соответствующий читаемый, передний итератор! Для рабочего примера его использования см.эту программу.

A constant node_iterator

Теперь нашnode_iteratorпредоставляет клиентам доступ как кnode'sprint [std::ostream&]constчленской функции, так и к его мутирующемуdouble_me()члену. Если бы мы хотели построить константуnode_iterator, нам пришлось бы внести только три изменения:

class const_node_iterator
  : public boost::iterator_facade<
        const_node_iterator
      , node_base const
      , boost::forward_traversal_tag
    >
{
 public:
    const_node_iterator()
      : m_node(0) {}
    explicit const_node_iterator(node_base* p)
      : m_node(p) {}
 private:
    friend class boost::iterator_core_access;
    void increment() { m_node = m_node->next(); }
    bool equal(const_node_iterator const& other) const
    {
        return this->m_node == other.m_node;
    }
    node_base const& dereference() const { return *m_node; }
    node_base const* m_node;
};

На самом делеnode_iteratorиconst_node_iteratorнастолько похожи, что имеет смысл разложить общий код на шаблон следующим образом:

template <class Value>
class node_iter
  : public boost::iterator_facade<
        node_iter<Value>
      , Value
      , boost::forward_traversal_tag
    >
{
 public:
    node_iter()
      : m_node(0) {}
    explicit node_iter(Value* p)
      : m_node(p) {}
 private:
    friend class boost::iterator_core_access;
    bool equal(node_iter<Value> const& other) const
    {
        return this->m_node == other.m_node;
    }
    void increment()
    { m_node = m_node->next(); }
    Value& dereference() const
    { return *m_node; }
    Value* m_node;
};
typedef node_iter<node_base> node_iterator;
typedef node_iter<node_base const> node_const_iterator;

Interoperability

Нашconst_node_iteratorотлично работает сам по себе, но вместе сnode_iteratorон не совсем соответствует ожиданиям. Например, мы хотели бы иметь возможность пройтиnode_iterator, где ожидалосьnode_const_iterator, так же, как вы можете сstd::listiteratorиconst_iterator. Кроме того, учитываяnode_iteratorиnode_const_iteratorв одном списке, мы должны быть в состоянии сравнить их для равенства.

Эта ожидаемая способность использовать два различных типа итераторов вместе известна каксовместимость. Достижение функциональной совместимости в нашем случае так же просто, как и уравнение.функция и добавление шаблонного преобразующего конструктора34:

template <class Value>
class node_iter
  : public boost::iterator_facade<
        node_iter<Value>
      , Value
      , boost::forward_traversal_tag
    >
{
 public:
    node_iter()
      : m_node(0) {}
    explicit node_iter(Value* p)
      : m_node(p) {}
    template <class OtherValue>
    node_iter(node_iter<OtherValue> const& other)
      : m_node(other.m_node) {}
 private:
    friend class boost::iterator_core_access;
    template <class> friend class node_iter;
    template <class OtherValue>
    bool equal(node_iter<OtherValue> const& other) const
    {
        return this->m_node == other.m_node;
    }
    void increment()
    { m_node = m_node->next(); }
    Value& dereference() const
    { return *m_node; }
    Value* m_node;
};
typedef impl::node_iterator<node_base> node_iterator;
typedef impl::node_iterator<node_base const> node_const_iterator;
[3]Если вы используете старый компилятор и он не может справиться с этим примером, см. примерный коддля обходных путей.
[4]Если быnode_iteratorбылитератором случайного доступа, нам пришлось бы также использовать егоdistance_to.

Вы можете увидеть пример программы, которая выполняет наши совместимые итераторыздесь.

Telling the Truth

Теперьnode_iteratorиnode_const_iteratorведут себя точно так, как вы ожидаете... почти. Мы можем сравнить их и преобразовать в одном направлении: отnode_iteratorдоnode_const_iterator. Если мы попытаемся преобразовать изnode_const_iteratorвnode_iterator, мы получим ошибку, когда преобразующий конструктор попытается инициализироватьnode_iterator'sm_node, узелс узеломconst*. Так в чем проблема?

Проблема в том, чтоboost:::: значениебудетистинным, но оно должно бытьложным.является_конвертируемымложью, потому что он может видеть только додекларацииконструктора преобразования, но не может заглянуть внутрь определения, чтобы убедиться, что он будет компилировать. Идеальное решение может привести к тому, что конструктор преобразованияnode_iterисчезнет, когда конверсияm_nodeвыйдет из строя.

На самом деле, этот вид магии возможен с помощьюповышения::enable_if. Переписывая преобразующий конструктор следующим образом, мы можем удалить его из набора перегрузок, когда это не подходит:

#include <boost/type_traits/is_convertible.hpp>
#include <boost/utility/enable_if.hpp>
  ...
private:
  struct enabler {};
public:
  template <class OtherValue>
  node_iter(
      node_iter<OtherValue> const& other
    , typename boost::enable_if<
          boost::is_convertible<OtherValue*,Value*>
        , enabler
      >::type = enabler()
  )
    : m_node(other.m_node) {}

Wrap Up

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

Статья Iterator Facade раздела может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-07-05 00:50:09/0.0059080123901367/0