<html>
<head><title>N4124, Toward More Expressive Iterator Tags</title></head>
<body>
<table border=0>
<tr><td><b>Doc No:</b></td><td>N4124</td></tr>
<tr><td><b>Date:</b></td><td>2014-09-11</td></tr>
<tr><td><b>Reply to:</b></td><td><tt>stdbill.h@pobox.com</tt></td></tr>
</table>
<center>
<h2>Toward More Expressive Iterator Tags</h2>
<h3>Bill Seymour<br>2014-09-11</h3>
</center>
<hr>
<h3>Introduction</h3>

During the discussion of <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3976.html">N3976</a>
in Rapperswil, it was pointed out that certain iterators proposed in the paper claim to be
random-access iterators when, in fact, they&rsquo;re not even forward iterators.
A straw poll showed that a majority of those present were in favor of keeping
<tt>random_access_iterator_tag</tt> as the iterator category (because the more
efficient algorithms would &ldquo;probably just work&rdquo;); but there were
enough dissenting votes that calling it consensus might be a bit of a stretch.

<p>The problem is probably best solved using concepts; but we already have
iterator tags and they&rsquo;re not going away.  This short paper asks whether
there&rsquo;s interest in having more finely-grained iterator tags.
At this point, it&rsquo;s just a partly-baked idea intended to get a discussion started.

<p><hr>
<h3>Revision history</h3>
This paper revises <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4068.html">N4068</a> to:
<ul>
<li>reintroduce existing iterator tag inheritance that got lost in N4068.
<p><li>use the <tt>packer</tt> and <tt>contains_types</tt> templates proposed in
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4115.html">N4115</a>.
The <tt>packer</tt> template replaces N4068&rsquo;s <tt>basic_iterator_tag</tt>;
and the metaprogramming in the test code at the end of that paper,
which is something of a distraction, is no longer needed.
[Note:  N4115 will be revised for the pre-Urbana mailing, N4144;
but nothing in that revision affects this paper.]
</ul>

<p><hr>
<h3>Acknowledgement</h3>

Thanks to Stephan T. Lavavej for teaching me the metaprogramming trick
that I needed for writing the test code at the end of N4068,
which eventually led to N4115.

<p><hr>
<h3>The basic idea</h3>
We&rsquo;ll start with several new iterator tags that express particular iterator requirements.
This is probably not an exhaustive list.
<ul>
<li>Whether <tt>*<i>iter</i></tt> is an actual <tt>value_type&amp;</tt> (not a proxy)
and the value is not cached in the iterator itself:
<pre>
    struct reference_tag { };
</pre>

<p><li>Whether <tt>*<i>iter</i></tt> is a modifiable lvalue or an rvalue (could be both):
<pre>
    struct lvalue_tag { };
    struct rvalue_tag { };
</pre>

<p><li>Whether <tt><i>iter1</i></tt>&nbsp;<tt>==</tt>&nbsp;<tt><i>iter2</i></tt> is well-formed:
<pre>
    struct equality_comparable_tag { };
</pre>

<p><li>Whether <tt><i>iter1</i></tt>&nbsp;<tt>==</tt>&nbsp;<tt><i>iter2</i></tt>
       implies both <tt>++<i>iter1</i></tt>&nbsp;<tt>==</tt>&nbsp;<tt><i>++iter2</i></tt>
       and <tt>&amp;*<i>iter1</i></tt>&nbsp;<tt>==</tt>&nbsp;<tt>&amp;*<i>iter2</i></tt>
       (i.e., <tt>*<i>iter1</i></tt> and <tt>*<i>iter2</i></tt> are the same object):
<pre>
    struct multipass_tag { };
</pre>

<p><li>Whether <tt>--<i>iter</i></tt> is well-formed:
<pre>
    struct decrementable_tag { };
</pre>

<p><li>Whether <tt><i>iter</i></tt> can be moved an arbitrary distance
and is less-than comparable:
<pre>
    struct random_move_tag { };
</pre>
</ul>

For the most part, the iterator tags that we all know and love could become
<tt>typedef</tt>s of <tt>packer</tt>s
(see <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4115.html">N4115</a>):

<pre>
    typedef packer&lt;lvalue_tag&gt; output_iterator_tag;

    typedef packer&lt;rvalue_tag, equality_comparable_tag&gt; input_iterator_tag;
</pre>

But to avoid breaking legacy code that depends on iterator tag inheritance,
we could apply the inheritance to <tt>packer</tt> specializations:

<pre>
    template&lt;&gt;
    struct packer&lt;reference_tag,
                  lvalue_tag,
                  rvalue_tag,
                  equality_comparable_tag,
                  multipass_tag&gt;
             : input_iterator_tag { };
</pre>

And then (with the same parameter pack):

<pre>
    typedef packer&lt;reference_tag,
                   lvalue_tag,
                   rvalue_tag,
                   equality_comparable_tag,
                   multipass_tag&gt;
              forward_iterator_tag;
</pre>

And so on...

<pre>
    template&lt;&gt;
    struct packer&lt;reference_tag,
                  lvalue_tag,
                  rvalue_tag,
                  equality_comparable_tag,
                  multipass_tag,
                  decrementable_tag&gt;
             : forward_iterator_tag { };

    typedef packer&lt;reference_tag,
                   lvalue_tag,
                   rvalue_tag,
                   equality_comparable_tag,
                   multipass_tag,
                   decrementable_tag&gt;
              bidirectional_iterator_tag;

    template&lt;&gt;
    struct packer&lt;reference_tag,
                  lvalue_tag,
                  rvalue_tag,
                  equality_comparable_tag,
                  multipass_tag,
                  decrementable_tag,
                  random_move_tag&gt;
             : bidirectional_iterator_tag { };

    typedef packer&lt;reference_tag,
                   lvalue_tag,
                   rvalue_tag,
                   equality_comparable_tag,
                   multipass_tag,
                   decrementable_tag,
                   random_move_tag&gt;
              random_access_iterator_tag;
</pre>

Note that we could still say, for example,
<pre>
    template &lt;class T&gt;
    struct iterator_traits&lt;T*&gt;
    {
        // ...
        typedef random_access_iterator_tag iterator_category;
    };
</pre>
without breaking legacy code.

<p><hr>
<h3>A couple of use cases</h3>
<ul>
<li>One rather obvious example would seem to be the iterator we get from
<nobr><tt>vector&lt;bool&gt;</tt>,</nobr> not even a forward iterator
because <tt>*<i>iter</i></tt> is a proxy instead of a proper <tt>bool&amp;</tt>
(so it lacks <tt>reference_tag</tt> in its parameter pack);
but it still has the random-movement ability for use by altorithms for which
the proxy isn&rsquo;t a problem:
<pre>
    template&lt;&gt;
    struct packer&lt;lvalue_tag,
                  rvalue_tag,
                  equality_comparable_tag,
                  multipass_tag,
                  decrementable_tag,
                  random_move_tag&gt;
             : input_iterator_tag, output_iterator_tag { };

    typedef packer&lt;lvalue_tag,
                   rvalue_tag,
                   equality_comparable_tag,
                   multipass_tag,
                   decrementable_tag,
                   random_move_tag&gt;
              random_proxy_iterator_tag;
</pre>

<p><li>And let&rsquo;s say that somebody wants to model as C++ iterators
what the database folk call scrolling cursors:
<pre>
    template&lt;&gt;
    struct packer&lt;rvalue_tag,
                  equality_comparable_tag,
                  multipass_tag,
                  decrementable_tag,
                  random_move_tag&gt;
             : input_iterator_tag { };

    typedef packer&lt;rvalue_tag,
                   equality_comparable_tag,
                   multipass_tag,
                   decrementable_tag,
                   random_move_tag&gt;
              scrolling_cursor_tag;
</pre>
A scrolling cursor can move back and forth by arbitrary distances;
but it lacks both <tt>reference_tag</tt> and <tt>lvalue_tag</tt>
because <tt>*<i>iter</i></tt> is probably a <tt>const</tt> reference
to a value cached in the iterator itself.

</ul>

<p><hr>
<h3>A crude test of the basic idea</h3>

The author&rsquo;s actual code, in the public domain, is available
<a href="http://www.cstdbill.com/ansi/cpp/itercat/n4124-test.cpp">here</a>
if you want to try it yourself.

<pre>
#include &lt;iostream&gt;

//
// Assume that &lt;type_traits&gt; and &lt;iterator&gt; define the new
// templates proposed in N4115 and this paper, respectively,
// in the std::experimental namespace, along with the usual
// iterator_traits template for which the &lt;T*&gt; specialization
// uses the new std::experimental::random_access_iterator_tag.
//
#include &lt;type_traits&gt;
#include &lt;iterator&gt;

namespace {

using std::cout;
using std::experimental::packer;
using std::experimental::contains_types;
using std::experimental::random_move_tag;

namespace detail {

//
// Dispatch on true_type/false_type<!-- (see <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4115.html">N4115</a>)-->:
//
template&lt;class Iter&gt; void my_algorithm(Iter, std::false_type)
{
    cout &lt;&lt; "Less efficient\n";
}
template&lt;class Iter&gt; void my_algorithm(Iter, std::true_type)
{
    cout &lt;&lt; "More efficient\n";
}

typedef packer&lt;random_move_tag&gt; mininum_fast_iterator_tag;

//
// Dispatch on the iterator category:
//
template&lt;class Iter&gt;
void my_algorithm(Iter, std::experimental::forward_iterator_tag)
{
    cout &lt;&lt; "Slower legacy code still works\n";
}
template&lt;class Iter&gt;
void my_algorithm(Iter, std::experimental::random_access_iterator_tag)
{
    cout &lt;&lt; "Faster legacy code still works\n";
}

} // namespace detail

template&lt;class Iter&gt; void my_algorithm(Iter first)
{
    detail::my_algorithm(first,
      contains_types&lt;
        typename std::experimental::iterator_traits&lt;Iter&gt;::iterator_category,
        detail::mininum_fast_iterator_tag&gt;());
}

template&lt;class Iter&gt; void legacy_algorithm(Iter first)
{
    detail::my_algorithm(first,
      typename std::experimental::iterator_traits&lt;Iter&gt;::iterator_category());
}

struct my_forward_iterator
{
    // ...
    typedef std::experimental::forward_iterator_tag iterator_category;
};

struct my_bidirectional_iterator
{
    // ...
    typedef std::experimental::bidirectional_iterator_tag iterator_category;
};

struct my_random_iterator
{
    // ...
    typedef std::experimental::random_access_iterator_tag iterator_category;
};

} // anonymous namespace

int main()
{
    my_algorithm(my_forward_iterator());
    my_algorithm(my_random_iterator());
    my_algorithm("char*");

    legacy_algorithm(my_forward_iterator());
    legacy_algorithm(my_bidirectional_iterator());

    static int array[] = { 0 };
    legacy_algorithm(array);
}
</pre>

Expected output:
<pre>
Less efficient
More efficient
More efficient
Slower legacy code still works
Slower legacy code still works
Faster legacy code still works
</pre>

<p><hr>
Reply to:&nbsp;&nbsp;<tt>stdbill.h@pobox.com</tt>

</body>
</html>
