| Doc No: | N4124 |
| Date: | 2014-09-11 |
| Reply to: | stdbill.h@pobox.com |
The problem is probably best solved using concepts; but we already have iterator tags and they’re not going away. This short paper asks whether there’s interest in having more finely-grained iterator tags. At this point, it’s just a partly-baked idea intended to get a discussion started.
struct reference_tag { };
struct lvalue_tag { };
struct rvalue_tag { };
struct equality_comparable_tag { };
struct multipass_tag { };
struct decrementable_tag { };
struct random_move_tag { };
typedef packer<lvalue_tag> output_iterator_tag;
typedef packer<rvalue_tag, equality_comparable_tag> input_iterator_tag;
But to avoid breaking legacy code that depends on iterator tag inheritance,
we could apply the inheritance to packer specializations:
template<>
struct packer<reference_tag,
lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag>
: input_iterator_tag { };
And then (with the same parameter pack):
typedef packer<reference_tag,
lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag>
forward_iterator_tag;
And so on...
template<>
struct packer<reference_tag,
lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag>
: forward_iterator_tag { };
typedef packer<reference_tag,
lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag>
bidirectional_iterator_tag;
template<>
struct packer<reference_tag,
lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag,
random_move_tag>
: bidirectional_iterator_tag { };
typedef packer<reference_tag,
lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag,
random_move_tag>
random_access_iterator_tag;
Note that we could still say, for example,
template <class T>
struct iterator_traits<T*>
{
// ...
typedef random_access_iterator_tag iterator_category;
};
without breaking legacy code.
template<>
struct packer<lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag,
random_move_tag>
: input_iterator_tag, output_iterator_tag { };
typedef packer<lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag,
random_move_tag>
random_proxy_iterator_tag;
template<>
struct packer<rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag,
random_move_tag>
: input_iterator_tag { };
typedef packer<rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag,
random_move_tag>
scrolling_cursor_tag;
A scrolling cursor can move back and forth by arbitrary distances;
but it lacks both reference_tag and lvalue_tag
because *iter is probably a const reference
to a value cached in the iterator itself.
#include <iostream>
//
// Assume that <type_traits> and <iterator> 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 <T*> specialization
// uses the new std::experimental::random_access_iterator_tag.
//
#include <type_traits>
#include <iterator>
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:
//
template<class Iter> void my_algorithm(Iter, std::false_type)
{
cout << "Less efficient\n";
}
template<class Iter> void my_algorithm(Iter, std::true_type)
{
cout << "More efficient\n";
}
typedef packer<random_move_tag> mininum_fast_iterator_tag;
//
// Dispatch on the iterator category:
//
template<class Iter>
void my_algorithm(Iter, std::experimental::forward_iterator_tag)
{
cout << "Slower legacy code still works\n";
}
template<class Iter>
void my_algorithm(Iter, std::experimental::random_access_iterator_tag)
{
cout << "Faster legacy code still works\n";
}
} // namespace detail
template<class Iter> void my_algorithm(Iter first)
{
detail::my_algorithm(first,
contains_types<
typename std::experimental::iterator_traits<Iter>::iterator_category,
detail::mininum_fast_iterator_tag>());
}
template<class Iter> void legacy_algorithm(Iter first)
{
detail::my_algorithm(first,
typename std::experimental::iterator_traits<Iter>::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);
}
Expected output:
Less efficient More efficient More efficient Slower legacy code still works Slower legacy code still works Faster legacy code still works