| Doc No: | N4068 |
| Date: | 2014-06-28 |
| 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 { };
template<class... Tags> struct basic_iterator_tag { };
And then the existing iterator tags that we all know and love could become:
typedef basic_iterator_tag<lvalue_tag> output_iterator_tag;
typedef basic_iterator_tag<rvalue_tag, equality_comparable_tag>
input_iterator_tag;
typedef basic_iterator_tag<reference_tag,
lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag>
forward_iterator_tag;
typedef basic_iterator_tag<reference_tag,
lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag>
bidirectional_iterator_tag;
typedef basic_iterator_tag<reference_tag,
lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag,
random_move_tag>
random_access_iterator_tag;
typedef basic_iterator_tag<lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag,
random_move_tag>
vector_bool_iterator_tag;
Note that it lacks reference_tag in the parameter pack because *iter
returns a proxy instead of a proper bool&.
typedef basic_iterator_tag<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 probably returns by const reference
a value cached in the iterator itself.
#include <iostream>
#include <type_traits>
namespace std {
namespace experimental {
//
// The proposed iterator tags:
//
struct reference_tag { };
struct lvalue_tag { };
struct rvalue_tag { };
struct equality_comparable_tag { };
struct multipass_tag { };
struct decrementable_tag { };
struct random_move_tag { };
template<class... Tags> struct basic_iterator_tag { };
//
// The existing iterator tags:
//
typedef basic_iterator_tag<lvalue_tag> output_iterator_tag;
typedef basic_iterator_tag<rvalue_tag, equality_comparable_tag>
input_iterator_tag;
typedef basic_iterator_tag<reference_tag,
lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag>
forward_iterator_tag;
typedef basic_iterator_tag<reference_tag,
lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag>
bidirectional_iterator_tag;
typedef basic_iterator_tag<reference_tag,
lvalue_tag,
rvalue_tag,
equality_comparable_tag,
multipass_tag,
decrementable_tag,
random_move_tag>
random_access_iterator_tag;
//
// Whether a parameter pack contains a given type
// (probably an implementation detail in the standard library):
//
template <class Required, class... Present> struct _Pack_has_type;
template <class Required> struct _Pack_has_type<Required> : false_type { };
template <class Required, class... Rest>
struct _Pack_has_type<Required, Required, Rest...> : true_type { };
template <class Required, class First, class... Rest>
struct _Pack_has_type<Required, First, Rest...>
: _Pack_has_type<Required, Rest...> { };
//
// Whether a basic_iterator_tag (Candidate) has a parameter pack that’s
// a superset of the pack of some minimum basic_iterator_tag (Required):
//
template <class Candidate, class Required> struct has_iterator_tags;
template <class... Candidates>
struct has_iterator_tags<basic_iterator_tag<Candidates...>,
basic_iterator_tag<>>
: true_type { };
template <class... Candidates, class Reqd1, class... ReqdRest>
struct has_iterator_tags<basic_iterator_tag<Candidates...>,
basic_iterator_tag<Reqd1, ReqdRest...>>
: integral_constant<bool,
_Pack_has_type<Reqd1, Candidates...>::value &&
has_iterator_tags<basic_iterator_tag<Candidates...>,
basic_iterator_tag<ReqdRest...>>::value> { };
} // namespace experimental
} // namespace std
namespace {
using std::experimental::has_iterator_tags;
using std::experimental::basic_iterator_tag;
using std::experimental::random_move_tag;
namespace detail
{
template<class Iter> void my_algorithm(Iter, std::false_type)
{
std::cout << "Less efficient\n";
}
template<class Iter> void my_algorithm(Iter, std::true_type)
{
std::cout << "More efficient\n";
}
}
template<class Iter> void my_algorithm(Iter first)
{
detail::my_algorithm(first,
has_iterator_tags<typename Iter::iterator_category,
basic_iterator_tag<random_move_tag>>());
}
struct my_forward_iterator
{
typedef std::experimental::forward_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());
}