facade_tutorial.qbk

来自「Boost provides free peer-reviewed portab」· QBK 代码 · 共 508 行 · 第 1/2 页

QBK
508
字号
[section:facade_tutorial Tutorial]In this section we'll walk through the implementation of a fewiterators using `iterator_facade`, based around the simpleexample of a linked list of polymorphic objects.  This example wasinspired by a [@http://thread.gmane.org/gmane.comp.lib.boost.user/5100 `posting`]by Keith Macdonald on the [@http://www.boost.org/more/mailing_lists.htm#users `Boost-Users`]mailing list.[h2 The Problem]Say we've written a polymorphic linked list node base class:  # 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;  };Lists can hold objects of different types by linking togetherspecializations of the following template:  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;  };And we can print any node using the following streaming operator:  inline std::ostream& operator<<(std::ostream& s, node_base const& n)  {      n.print(s);      return s;  }Our first challenge is to build an appropriate iterator over theselists.[h2 A Basic Iterator Using `iterator_facade`]We will construct a `node_iterator` class using inheritance from`iterator_facade` to implement most of the iterator's operations.  # include "node.hpp"  # include <boost/iterator/iterator_facade.hpp>  class node_iterator    : public boost::iterator_facade<...>  {     ...  };[h2 Template Arguments for `iterator_facade`]`iterator_facade` has several template parameters, so we must decidewhat types to use for the arguments. The parameters are `Derived`,`Value`, `CategoryOrTraversal`, `Reference`, and `Difference`.[h3 `Derived`]Because `iterator_facade` is meant to be used with the CRTP[Cop95]_ the first parameter is the iterator class name itself,`node_iterator`.[h3 `Value`]The `Value` parameter determines the `node_iterator`\ 's`value_type`.  In this case, we are iterating over `node_base`objects, so `Value` will be `node_base`.[h3 `CategoryOrTraversal`]Now we have to determine which `iterator traversal concept`_ our`node_iterator` is going to model.  Singly-linked lists only haveforward links, so our iterator can't can't be a `bidirectionaltraversal iterator`_.  Our iterator should be able to make multiplepasses over the same linked list (unlike, say, an`istream_iterator` which consumes the stream it traverses), so itmust be a `forward traversal iterator`_.  Therefore, we'll pass`boost::forward_traversal_tag` in this position [#category]_... [#category] `iterator_facade` also supports old-style category   tags, so we could have passed `std::forward_iterator_tag` here;   either way, the resulting iterator's `iterator_category` will   end up being `std::forward_iterator_tag`.[h3 `Reference`]The `Reference` argument becomes the type returned by`node_iterator`\ 's dereference operation, and will also be thesame as `std::iterator_traits<node_iterator>::reference`.  Thelibrary's default for this parameter is `Value&`; since`node_base&` is a good choice for the iterator's `reference`type, we can omit this argument, or pass `use_default`.[h3 `Difference`]The `Difference` argument determines how the distance betweentwo `node_iterator`\ s will be measured and will also be thesame as `std::iterator_traits<node_iterator>::difference_type`.The library's default for `Difference` is `std::ptrdiff_t`, anappropriate type for measuring the distance between any twoaddresses in memory, and one that works for almost any iterator,so we can omit this argument, too.The declaration of `node_iterator` will therefore look somethinglike:  # include "node.hpp"  # include <boost/iterator/iterator_facade.hpp>  class node_iterator    : public boost::iterator_facade<          node_iterator        , node_base        , boost::forward_traversal_tag      >  {     ...  };[h2 Constructors and Data Members]Next we need to decide how to represent the iterator's position.This representation will take the form of data members, so we'llalso need to write constructors to initialize them.  The`node_iterator`\ 's position is quite naturally represented usinga pointer to a `node_base`.  We'll need a constructor to build aniterator from a `node_base*`, and a default constructor tosatisfy the `forward traversal iterator`_ requirements [#default]_.Our `node_iterator` then becomes:  # 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;  };.. [#default] Technically, the C++ standard places almost no   requirements on a default-constructed iterator, so if we were   really concerned with efficiency, we could've written the   default constructor to leave `m_node` uninitialized.[h2 Implementing the Core Operations]The last step is to implement the `core operations`_ required bythe concepts we want our iterator to model.  Referring to thetable__, we can see that the first three rows are applicablebecause `node_iterator` needs to satisfy the requirements for`readable iterator`_, `single pass iterator`_, and `incrementableiterator`_.  __ `core operations`_We therefore need to supply `dereference`,`equal`, and `increment` members.  We don't want these membersto become part of `node_iterator`\ 's public interface, so we canmake them private and grant friendship to`boost::iterator_core_access`, a "back-door" that`iterator_facade` uses to get access to the core operations:  # 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;  };

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?