is_iterator<T> type trait

624 views
Skip to first unread message

Matthew Fioravante

unread,
Jun 10, 2015, 4:34:44 PM6/10/15
to std-pr...@isocpp.org
Sometimes its useful to know if a given type T is an iterator, particularly for implementing data structures which have constructors that accept iterator pairs which can conflict with other constructors. This can also be useful for algorithms with accept iterators. With is_iterator<T>, we can use SFINAE to limit overload conflicts.

Here is one possible implementation:

#include <type_traits>
#include <iterator>
#include <vector>
#include <utility>

template <typename T>
 
struct is_iterator {
 
static char test(...);

 
template <typename U,
   
typename=typename std::iterator_traits<U>::difference_type,
   
typename=typename std::iterator_traits<U>::pointer,
   
typename=typename std::iterator_traits<U>::reference,
   
typename=typename std::iterator_traits<U>::value_type,
   
typename=typename std::iterator_traits<U>::iterator_category
 
> static long test(U&&);

 
static bool value = std::is_same<decltype(test(std::declval<T>())),long>::value;
};

Do you think something like this is worth having in the standard library? Or will such a construct be completely obsoleted by concepts?

David Krauss

unread,
Jun 10, 2015, 8:23:18 PM6/10/15
to std-pr...@isocpp.org

On 2015–06–11, at 4:34 AM, Matthew Fioravante <fmatth...@gmail.com> wrote:

Here is one possible implementation:

What class is going to define iterator_traits<T>::iterator_category without intending to be an iterator?

Just use std::enable_if_t< std::is_base_of_v< std::input_iterator_tag, std::iterator_traits<T>::iterator_category > >.

Or output_iterator_tag. Seldom does anything take an input or an output without caring which, but you can OR such conditions together to be explicit about the ambiguity.

Matthew Fioravante

unread,
Jun 10, 2015, 8:30:28 PM6/10/15
to std-pr...@isocpp.org


On Wednesday, June 10, 2015 at 8:23:18 PM UTC-4, David Krauss wrote:
Just use std::enable_if_t< std::is_base_of_v< std::input_iterator_tag, std::iterator_traits<T>::iterator_category > >.

I still think its better to have a usability wrapper. Your solution works but its less obvious what its doing from a quick glance. A wrapper such as listed below clearly states what condition is being tested and makes the code more readable and obvious.

is_iterator
is_input_iterator
is_output_iterator
is_forward_iterator
is_bidirectional_iterator
is_random_access_iterator
is_contiguous_iterator

David Krauss

unread,
Jun 10, 2015, 9:20:04 PM6/10/15
to std-pr...@isocpp.org, Matthew Fioravante
On 2015–06–11, at 8:30 AM, Matthew Fioravante <fmatth...@gmail.com> wrote:



On Wednesday, June 10, 2015 at 8:23:18 PM UTC-4, David Krauss wrote:
Just use std::enable_if_t< std::is_base_of_v< std::input_iterator_tag, std::iterator_traits<T>::iterator_category > >.

I still think its better to have a usability wrapper. Your solution works but its less obvious what its doing from a quick glance. A wrapper such as listed below clearly states what condition is being tested and makes the code more readable and obvious.

That’s true of any SFINAE, hence the Concepts proposal. The question is, are these predicates more common than any other SFINAE condition?

Also, note that the point of dispatch tags in the first place is to avoid SFINAE. (Or, since tags are an older technique, the point of enable_if is to fall back when tags don’t work.) So you probably shouldn’t be querying each individual category with SFINAE. An external interface would be rather odd to treat BidirectionalIterator as an iterator but a ForwardIterator as a generic value. An internal interface should usually take any iterator, break out the tags, and static_assert if the iterator isn’t good enough.
Reply all
Reply to author
Forward
0 new messages