Boost C++ 库

……世界上最受推崇且设计最专业的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu,《C++ 编码规范

迭代器外观 - Boost C++ 函数库

迭代器外观

作者 David Abrahams, Jeremy Siek, Thomas Witt
联系方式 dave@boost-consulting.com, jsiek@osl.iu.edu, witt@ive.uni-hannover.de
组织 Boost Consulting, 印第安纳大学 Open Systems Lab, 汉诺威大学 运输铁路运营与建设研究所
日期 2006-09-11
版权 Copyright David Abrahams, Jeremy Siek, and Thomas Witt 2003.
摘要 iterator_facade是一个基类模板,它根据派生迭代器类提供的几个核心函数和关联类型来实现标准迭代器的接口。

概述

尽管迭代器接口非常丰富,但有一个核心子集是所有功能所必需的。我们已经确定了迭代器的以下核心行为:

  • 解引用
  • 递增
  • 递减
  • 相等比较
  • 随机访问移动
  • 距离测量

除了上述行为外,核心接口元素还包括通过迭代器特征暴露的关联类型value_type, reference, difference_typeiterator_category.

迭代器外观(Iterator facade)使用了奇异递归模板模式(CRTP) [Cop95],以便用户可以指定iterator_facade以前的设计使用策略对象来指定行为,但由于几个原因,该方法已被放弃:

  1. 策略对象的创建和最终复制可能会产生开销,而当前方法可以避免这种开销。
  2. 策略对象方法不允许对创建的迭代器类型进行自定义构造函数,这对于iterator_facade在其他库实现中使用是必不可少的。
  3. 如果未使用 CRTP,则标准要求迭代器的operator++返回迭代器类型本身,这将意味着所有使用该库构建的迭代器都必须是iterator_facade<...>的特化,而不是更具描述性的名称,如indirect_iterator<T*>。需要笨拙的类型生成元函数来构建新的参数化迭代器,并且一个单独的iterator_adaptor层将是不可能的。

用法

The user ofiterator_facade将他的迭代器类派生自iterator_facade的特化,并将派生迭代器类作为iterator_facade的第一个模板参数。其他模板参数的顺序经过精心挑选,以便利用有用的默认值。例如,在定义常量左值迭代器时,用户可以传递迭代器的 const 限定版本value_typeiterator_facade参数,并省略后面的参考参数。

的行为。派生迭代器类必须定义实现迭代器核心行为的成员函数。下表描述了根据派生迭代器类型类别要求有效的表达式。这些成员函数将在下文简要描述,并在迭代器外观要求中更详细地描述。

表达式 效果
i.dereference() 访问被引用的值
i.equal(j) 比较相等性与j
i.increment() 向前移动一个位置
i.decrement() 向后移动一个位置
i.advance(n) 前进n位置
i.distance_to(j) 测量到j

的距离。除了实现核心接口函数外,派生自iterator_facade的迭代器通常会定义几个构造函数。为了模拟任何标准迭代器概念,迭代器至少必须有一个复制构造函数。此外,如果迭代器类型X旨在与另一个迭代器类型自动互操作Y(例如常量迭代器和可变迭代器)那么必须有一个从XY或从YX(但不是两者)的隐式转换,通常实现为转换构造函数。最后,如果迭代器要模拟 Forward Traversal Iterator 或更精细的迭代器概念,则需要默认构造函数。

迭代器核心访问

iterator_facade并且操作符实现需要能够访问派生类中的核心成员函数。将核心成员函数公开会向用户暴露实现细节。此处使用的设计确保实现细节不会出现在派生迭代器类型的公共接口中。

防止直接访问核心成员函数有两个优点。首先,用户不可能在打算使用 value_type 的成员时意外地使用迭代器的成员函数。这在过去的智能指针实现中一直是一个问题。第二个也是主要的优点是,库实现者可以自由地用基于iterator_facade的实现,而无需担心破坏直接访问公共核心成员函数的代码。

在朴素实现中,将派生类的核心成员函数保持为 private 将需要授予iterator_facade和所有七个运算符的友元关系。为了减轻限制访问的负担,iterator_core_access被提供,这是一个充当派生迭代器类中核心成员函数网关的类。派生类的作者只需授予iterator_core_access友元关系即可使其核心成员函数可供库使用。

iterator_core_access通常实现为空类,仅包含调用迭代器核心成员函数的私有静态成员函数。但是,没有必要标准化网关协议。请注意,即使iterator_core_access使用了 public 成员函数,也不会打开安全漏洞,因为每个核心成员函数都保留了迭代器的不变量。

operator[]

通用迭代器的索引运算符提出了特殊挑战。随机访问迭代器的operator[]仅要求返回可转换为其value_type的行为。要求其返回左值将会排除掉目前合法的、将引用值存储在数据成员中的随机访问迭代器(例如 counting_iterator),因为*(p+n)是临时迭代器p+n的一个引用,该临时迭代器在operator[]返回时被销毁。

使用iterator_facade实现 issue 299 的首选决议并由提案 n1550 采纳的语义:其结果p[n]的结果是一个可转换为迭代器value_typep[n] = x等效于*(p + n) = x(注意:此结果对象可能实现为包含p+n副本的代理)。这种方法将适用于任何随机访问迭代器,而与其实施的其他细节无关。知道其迭代器实现细节的用户可以自由地在派生迭代器类中实现一个operator[],它返回一个左值;它将隐藏由iterator_facade提供的那个,使其对她的迭代器的客户端可用。

operator->

reference可读迭代器(以及今天的输入迭代器)的类型不必是引用,只要它可以转换为迭代器的value_type即可。但是,当value_type是类时,仍然必须能够通过operator->访问成员。因此,其reference类型实际上不是引用的迭代器必须从其operator->.

返回一个包含被引用值副本的代理。iterator_facadeoperator->andoperator[]的返回类型没有明确指定。相反,这些类型根据一组要求进行描述,这些要求必须由iterator_facade实现。

[Cop95](1, 2) [Coplien, 1995] Coplien, J., Curiously Recurring Template Patterns, C++ Report, February 1995, pp. 24-27.

参考

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&);

iterator_category成员iterator_facade

iterator-category(CategoryOrTraversal, value_type, reference)

其中 *iterator-category* 定义如下:

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上面使用的模板仅用于说明目的。成员运算符应仅在重载集中提供,前提是派生类型Dr1andDr2是可互操作的,这意味着至少一种类型可转换为另一种类型。该enable_if_interoperable方法使用 SFINAE 在类型不可互操作时将运算符从重载集中移除。运算符应表现得*好像*enable_if_interoperable被定义为

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要求

以下表格描述了在iterator_facadeDerived参数上典型的有效表达式,具体取决于它将模拟的迭代器概念。第一列中的操作必须可以通过类成员函数访问iterator_core_access。此外,static_cast<Derived*>(iterator_facade*)应该能够良好地构成。

在下表中,Fiterator_facade<X,V,C,R,D>, a是一个对象,类型为X, bandc是类型为const X, n是一个对象,其类型为F::difference_type, y是一个单次遍历迭代器类型的常量对象,它与Xz可互操作。是一个随机访问遍历迭代器类型的常量对象,它与X.

iterator_facade可互操作。核心操作

表达式 返回类型 断言/注意 用于实现迭代器概念(s)
c.dereference() F::reference   可读迭代器,可写迭代器
c.equal(y) 可转换为 bool 当且仅当candy指向相同位置。 单次遍历迭代器
a.increment() 未使用   可增量迭代器
a.decrement() 未使用   双向遍历迭代器
a.advance(n) 未使用   随机访问遍历迭代器
c.distance_to(z) 可转换为F::difference_type 等同于distance(c, X(z)). 随机访问遍历迭代器

iterator_facade操作

此部分中的操作根据Derived的核心接口上的操作进行描述,这些操作可能不可访问(即 private)。实现应通过类成员函数访问这些操作iterator_core_access.

reference operator*() const;

返回static_cast<Derived const*>(this)->dereference()

operator->() const;(参见 下方

返回

如果reference是引用类型,类型为pointer等于

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

否则返回一个类型未指定的对象,使得(*static_cast<Derived const*>(this))->m等效于(w = **static_cast<Derived const*>(this), w.m)对于某个临时对象w类型为value_type.

未指定 operator[](difference_type n) const;

返回一个可转换为value_type的对象。对于常量对象v类型为value_typen类型为difference_type, (*this)[n] = v等效于*(*this + n) = vstatic_cast<value_type const&>((*this)[n])等效于static_cast<value_type const&>(*(*this + n))

Derived& operator++();

效果
static_cast<Derived*>(this)->increment();
return *static_cast<Derived*>(this);

Derived operator++(int);

效果
Derived tmp(static_cast<Derived const*>(this));
++*this;
return tmp;

Derived& operator--();

效果
static_cast<Derived*>(this)->decrement();
return *static_cast<Derived*>(this);

Derived operator--(int);

效果
Derived tmp(static_cast<Derived const*>(this));
--*this;
return tmp;

Derived& operator+=(difference_type n);

效果
static_cast<Derived*>(this)->advance(n);
return *static_cast<Derived*>(this);

Derived& operator-=(difference_type n);

效果
static_cast<Derived*>(this)->advance(-n);
return *static_cast<Derived*>(this);

Derived operator-(difference_type n) const;

效果
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&);
效果
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);
返回

ifis_convertible<Dr2,Dr1>::value

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

否则,

((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);
返回

ifis_convertible<Dr2,Dr1>::value

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

否则,

!((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);
返回

ifis_convertible<Dr2,Dr1>::value

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

否则,

((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);
返回

ifis_convertible<Dr2,Dr1>::value

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

否则,

((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);
返回

ifis_convertible<Dr2,Dr1>::value

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

否则,

((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);
返回

ifis_convertible<Dr2,Dr1>::value

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

否则,

((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);
返回类型

ifis_convertible<Dr2,Dr1>::value

difference应为iterator_traits<Dr1>::difference_type.

否则

difference应为iterator_traits<Dr2>::difference_type

返回

ifis_convertible<Dr2,Dr1>::value

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

否则,

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

教程示例

在本节中,我们将演练使用iterator_facade实现几个迭代器的过程,以多态对象链表的简单示例为基础。此示例灵感来源于 Boost-Users 邮件列表上 Keith Macdonald 的一篇文章。

问题

假设我们已经编写了一个多态链表节点基类

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

我们的第一个挑战是构建一个合适的迭代器来遍历这些列表。

一个使用模板参数构建构造函数和数据成员的基础迭代器iterator_facade

我们将通过继承node_iterator来构建一个类iterator_facade,以实现该迭代器的大部分操作。

# include "node.hpp"
# include <boost/iterator/iterator_facade.hpp>

class node_iterator
  : public boost::iterator_facade<...>
{
   ...
};

模板参数iterator_facade

iterator_facade拥有多个模板参数,因此我们必须确定参数的具体类型。这些参数包括Derived, , CategoryOrTraversal, 参考差值.

Derived

因为iterator_facade旨在与 CRTP [Cop95] 一起使用,第一个参数是迭代器类名本身,node_iterator.

参数决定了node_iteratorvalue_type。在这种情况下,我们正在遍历node_base对象,因此将等于node_base.

CategoryOrTraversal

现在我们需要确定我们的node_iterator要建模哪种 迭代器遍历概念。单链表只有前向链接,所以我们的迭代器不能是 双向遍历迭代器。我们的迭代器应该能够对同一个链表进行多次遍历(不像例如istream_iterator会消耗其遍历的流),因此它必须是 前向遍历迭代器。因此,我们将在此位置传入boost::forward_traversal_tag1

[1]iterator_facade也支持旧式的类别标签,所以我们本可以传入std::forward_iterator_tag在此处;无论哪种方式,最终迭代器的iterator_category最终都将是std::forward_iterator_tag.

参考

参考参数成为node_iterator解引用操作返回的类型,并且也将与std::iterator_traits<node_iterator>::reference相同。库为此参数提供的默认值是Value&;由于node_base&是迭代器reference类型的一个好选择,我们可以省略此参数,或传入use_default.

差值

差值参数决定了两个node_iterator之间的距离将如何度量,并且也将与std::iterator_traits<node_iterator>::difference_type相同。库为差值std::ptrdiff_t提供的默认值是 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
    >
{
   ...
};

构造函数和数据成员

接下来,我们需要决定如何表示迭代器的位置。这种表示形式将采用数据成员的形式,因此我们还需要编写构造函数来初始化它们。node_iterator的位置很自然地使用一个指向node_base的指针来表示。我们需要一个构造函数来根据node_base*构建迭代器,以及一个默认构造函数来满足 前向遍历迭代器 的要求 2。我们的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
    >
{
 public:
    node_iterator()
      : m_node(0)
    {}

    explicit node_iterator(node_base* p)
      : m_node(p)
    {}

 private:
    ...
    node_base* m_node;
};
[2]从技术上讲,C++ 标准对默认构造的迭代器几乎没有要求,所以如果我们非常关注效率,我们可以将默认构造函数写成使m_node保持未初始化状态。

实现核心操作

最后一步是实现我们希望迭代器建模的概念所要求的 核心操作。参考 表格,我们可以看到前三行适用,因为node_iterator需要满足 可读迭代器单次遍历迭代器可增量迭代器 的要求。

因此我们需要提供dereference, equalincrement成员。我们不希望这些成员成为node_iterator公共接口的一部分,因此我们可以将它们设为私有并授予boost::iterator_core_access友元权限,这是一个“后门”,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;
};

瞧!一个完整且符合规范的可读、前向遍历迭代器就完成了!有关其用法的有效示例,请参阅 此程序

常量node_iterator

这可能也没什么帮助。node_iterator现在,我们的nodeprint(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_iteratorandconst_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;

互操作性

我们的const_node_iterator本身运行良好,但如果与node_iterator一起使用,它并不能完全达到预期。例如,我们希望能够传递一个node_iterator到期望node_const_iterator的地方,就像你对std::list<int>iteratorandconst_iterator所做的那样。此外,给定一个node_iteratornode_const_iterator到同一个列表中,我们应该能够比较它们是否相等。

这种将两种不同的迭代器类型一起使用的预期能力被称为 互操作性(interoperability)。在我们的例子中实现互操作性非常简单,只需将equal函数模板化,并添加一个模板化的转换构造函数 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

直言不讳

现在node_iteratorandnode_const_iterator函数模板化。node_iteratornode_const_iterator你可以在 这里 看到一个练习我们互操作迭代器的示例程序。node_const_iteratornode_iterator其行为完全符合你的预期……几乎是。我们可以比较它们,并且可以单向转换:从node_iteratorm_node转换到使用一个。如果我们尝试从转换到

,当转换构造函数尝试初始化boost:is_convertiblenode*将等于truenode const* 时,我们会得到一个错误。那么问题出在哪里呢?false问题在于node_iter<node_const_iterator,node_iterator>::valuenode_iter返回 true,但它应该返回 false。is_convertible 说谎了,因为它只能看到m_node转换构造函数的声明,却无法查看内部的定义来确保它能编译通过。一个完美的解决方案是当转换失败时,让

的转换构造函数消失。

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

总结

事实上,这种魔法可以通过使用 boost::enable_if 来实现。通过按如下方式重写转换构造函数,我们可以在它不适用时将其从重载集中移除iterator_facade本教程到此结束,但在你停止阅读之前,我们强烈建议你看一下 iterator_adaptor。还有另一种编写这些迭代器的方法,可能更为优越。