|  | 
|      | 
|  | 
| 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. 
 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 В приведенной ниже таблице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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||