![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
Iterator FacadeBoost , ,
|
Author: | Дэвид Абрахамс, Джереми Сик, Томас Витт | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Contact: | В то время как интерфейс итератора богат, есть базовое подмножество интерфейса, которое необходимо для всей функциональности. Мы определили следующие основные модели поведения итераторов:
В дополнение к поведению, перечисленному выше, основные элементы интерфейса включают связанные типы, подверженные через черты итератора:значение_тип,ссылка,Различие_типиитератор_категория. Фасад итератора использует Curiously Recurring Template Pattern (CRTP)[Cop95], чтобы пользователь мог указать поведениеiterator_facadeв производном классе. Бывшие проекты использовали объекты политики для определения поведения, но этот подход был отброшен по нескольким причинам:
UsageПользовательiterator_facadeполучает свой класс итератора из специализацииiterator_facadeи проходит класс производного итератора какiterator_facadeпервый параметр шаблона. Порядок других параметров шаблона был тщательно выбран, чтобы воспользоваться полезными по умолчанию. Например, при определении постоянного итератора lvalue пользователь может передать конст-квалифицированную версию параметраvalue_typeитератораiterator_facade'sValueи опустить параметр.Ссылкапараметр, который следует. Полученный класс итератора должен определять функции члена, реализующие основные модели поведения итератора. В следующей таблице описаны выражения, которые должны быть действительными в зависимости от категории производного типа итератора. Эти функции элементов кратко описаны ниже и более подробно в требованиях к фасаду итератора.
В дополнение к реализации основных функций интерфейса итератор, полученный изiterator_facade, обычно определяет несколько конструкторов. Чтобы смоделировать любую из стандартных концепций итератора, итератор должен иметь конструктор копий. Кроме того, если тип итератораXпредназначен для автоматического взаимодействия с другим типом итератораY(как с постоянными и изменяемыми итераторами), то должно быть неявное преобразование изXвYили изYвX(но не оба), обычно реализованное в качестве конструктора преобразования. Наконец, если итератор предназначен для моделирования итератора переднего поворота или более усовершенствованной концепции итератора, требуется конструктор по умолчанию. Iterator Core Accessiterator_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.
Referencetemplate < 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 В приведенной ниже таблицеFявляетсяитератором_фасада iterator_facade Core Operations
iterator_facade operationsОперации в этом разделе описаны в терминах операций на базовом интерфейсеПроизводный, который может быть недоступным (т.е. закрытым). Реализация должна обеспечивать доступ к этим операциям через функции-члены классаiterator_core_access. ссылкаоператор*()const;
оператор->()const;(см.ниже)
unspecified operator[](difference_type n) const;
Derived&operator++();
Выводитсяоператор++(int);
Производный &оператор--();
Производныйоператор - (int);
Производный &оператор +=(разница_типn);
Derived&operator-=(difference_typen);
Выводитсяоператор-(различие_типn)const;
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&);
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);
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,difference>::type operator -(iterator_facade<Dr1,V1,TC1,R1,D1> const& lhs, iterator_facade<Dr2,V2,TC2,R2,D2> const& rhs);
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_facadeiterator_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.
ReferenceАргументReferenceстановится типом, возвращаемымnode_iterator's dereference operation, а также будет таким же, какstd::iterator_traits DifferenceАргументРазницаопределяет, как будет измеряться расстояние между двумяnode_iterators, а также будет таким же, какstd::iterator_traits Декларация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; };
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::list Эта ожидаемая способность использовать два различных типа итераторов вместе известна каксовместимость. Достижение функциональной совместимости в нашем случае так же просто, как и уравнение.функция и добавление шаблонного преобразующего конструктора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;
Вы можете увидеть пример программы, которая выполняет наши совместимые итераторыздесь. Telling the TruthТеперьnode_iteratorиnode_const_iteratorведут себя точно так, как вы ожидаете... почти. Мы можем сравнить их и преобразовать в одном направлении: отnode_iteratorдоnode_const_iterator. Если мы попытаемся преобразовать изnode_const_iteratorвnode_iterator, мы получим ошибку, когда преобразующий конструктор попытается инициализироватьnode_iterator'sm_node, узелс узеломconst*. Так в чем проблема? Проблема в том, чтоboost:: На самом деле, этот вид магии возможен с помощьюповышения::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 |