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