Reputation: 42371
template<class T>
struct is_iterator
{
static const bool value = ??? // What to write ???
};
int main()
{
assert(false == is_iterator<int>::value);
assert(true == is_iterator<vector<int>::iterator>::value);
assert(true == is_iterator<list<int>::iterator>::value);
assert(true == is_iterator<string::iterator>::value);
assert(true == is_iterator<char*>::value); // a raw pointer is also an iterator
}
The question is: How to make the five assert statements pass?
Upvotes: 14
Views: 10304
Reputation: 2725
The proper way to detect any iterator would be with concepts, namely, std::input_or_output_iterator
.
Quoting cppreference:
template <class I> concept input_or_output_iterator = requires(I i) { { *i } -> /*can-reference*/; } && std::weakly_incrementable<I>;
The input_or_output_iterator concept forms the basis of the iterator concept taxonomy; every iterator type satisfies the input_or_output_iterator requirements.
Note that the exposition only type /*can-reference*/
really just means not void
, and can be accomplished like the following:
template <class T>
using with_ref_t = T &;
template <class T>
concept can_reference = requires() { typename detail::with_ref_t<T>; };
Upvotes: 3
Reputation: 55897
How about something like this?
template<typename T, typename = void>
struct is_iterator
{
static constexpr bool value = false;
};
template<typename T>
struct is_iterator<T, typename std::enable_if<!std::is_same<typename std::iterator_traits<T>::value_type, void>::value>::type>
{
static constexpr bool value = true;
};
example:
#include <iostream>
#include <type_traits>
#include <vector>
template<typename T, typename = void>
struct is_iterator
{
static constexpr bool value = false;
};
template<typename T>
struct is_iterator<T, typename std::enable_if<!std::is_same<typename std::iterator_traits<T>::value_type, void>::value>::type>
{
static constexpr bool value = true;
};
int main()
{
static_assert(!is_iterator<int>::value);
static_assert(is_iterator<int*>::value);
static_assert(is_iterator<std::vector<int>::iterator>::value);
}
http://liveworkspace.org/code/7dcf96c97fd0b7a69f12658fc7b2693e
Upvotes: 22
Reputation: 2870
Nothing new but a C++17 way of doing it :
#include <type_traits>
// default case
template <class T, class = void>
struct is_iterator : std::false_type
{
};
// specialization
template <class T>
struct is_iterator<T,
std::void_t<typename std::iterator_traits<T>::difference_type,
typename std::iterator_traits<T>::pointer,
typename std::iterator_traits<T>::reference,
typename std::iterator_traits<T>::value_type,
typename std::iterator_traits<T>::iterator_category>> : std::true_type
{
};
template <class T>
constexpr bool is_iterator_v = is_iterator<T>::value;
some tests:
#include <vector>
#include <list>
#include <map>
static_assert(is_iterator_v<std::vector<int>::iterator>);
static_assert(is_iterator_v<std::list<double>::const_iterator>);
static_assert(is_iterator_v<int*>);
static_assert(!is_iterator_v<std::list<double>>);
static_assert(!is_iterator_v<int>);
How it works:
std::false_type::value == false
std::true_type::value == true
std::void_t<X> <=> void
if X is a valid type. If not it will cause a substitution Failureis_iterator<X>
is seen as is_iterator<X, void>
If T
is an iterator then these types exist:
std::iterator_traits<T>::difference_type
std::iterator_traits<T>::pointer
std::iterator_traits<T>::reference
std::iterator_traits<T>::value_type
std::iterator_traits<T>::iterator_category
So std::void_t<...>
is void
.
The specialization match is_iterator<T,void>
(and also is_iterator<T>
) and inherit of std::true_type
If T
is not an iterator then at least one of the previous type doesn't exist, so std::void_t<...>
doesn't name a type and the whole specialization is an substitution Failure. So the only match for is_iterator
is the default case whom inherit of std::false_type
Upvotes: 2
Reputation: 3016
The original poster clarified that they are actually asking for a way to identify an InputIterator (see http://en.cppreference.com/w/cpp/concept/InputIterator) because they want to be able to increment and dereference the iterator. This has a very simple SFINAE solution in standard C++11, e.g. similar to that from the gcc STL:
template<typename InputIterator>
using RequireInputIterator = typename
std::enable_if<std::is_convertible<typename
std::iterator_traits<InputIterator>::iterator_category,
std::input_iterator_tag>::value>::type;
...
// Example: declare a vector constructor from a pair of input iterators.
template <typename InputIterator, typename = RequireInputIterator<InputIterator> >
MyVector(InputIterator first, InputIterator last) { /* ... */ };
This relies on the iterator type traits classes, which define the typedefs that Armen Tsirunyan thought were required of the iterators themselves. (The iterators can provide those typedefs, but they can also provide them in traits classes, which is necessary in order to use naked pointers as iterators, and the standard library implementations are required to do so.)
Upvotes: 2
Reputation: 303216
Coming in here a few years later, where C++11 and C++14 make it a lot easier to do such things. An iterator is, at its core, something that is dereferencable, incrementable. If it's an input iterator, then also comparable. Let's go with the latter - since that looks like what you want.
The simplest version would be to use void_t
:
template <typename... >
using void_t = void;
Base case:
template <typename T, typename = void>
struct is_input_iterator : std::false_type { };
Valid case specialization:
template <typename T>
struct is_input_iterator<T,
void_t<decltype(++std::declval<T&>()), // incrementable,
decltype(*std::declval<T&>()), // dereferencable,
decltype(std::declval<T&>() == std::declval<T&>())>> // comparable
: std::true_type { };
Alias:
template <typename T>
using is_input_iterator_t = typename is_input_iterator<T>::type;
No need to rely on iterator_category
or using the tedious C++03 style of check things using overload resolution. Expression SFINAE is where it's at.
As Mr. Wakely points out in the comments, [iterator.traits] requires that:
it is required that if
Iterator
is the type of an iterator, the typesiterator_traits<Iterator>::difference_type iterator_traits<Iterator>::value_type iterator_traits<Iterator>::iterator_category
be defined as the iterator’s difference type, value type and iterator category, respectively.
So we can define our iterator trait to simply check for that:
template <class T, class = void>
struct is_iterator : std::false_type { };
template <class T>
struct is_iterator<T, void_t<
typename std::iterator_traits<T>::iterator_category
>> : std::true_type { };
If iterator_traits<T>::iterator_category
is ill-formed, then T
is not an iterator.
Upvotes: 18
Reputation: 1498
I believe this should be a complete solution. Try it on http://gcc.godbolt.org and see the resulting assembly for the test functions.
#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&&);
constexpr static bool value = std::is_same<decltype(test(std::declval<T>())),long>::value;
};
struct Foo {};
//Returns true
bool f() { return is_iterator<typename std::vector<int>::iterator>::value; }
//Returns true
bool fc() { return is_iterator<typename std::vector<int>::const_iterator>::value; }
//Returns true
bool fr() { return is_iterator<typename std::vector<int>::reverse_iterator>::value; }
//Returns true
bool fcr() { return is_iterator<typename std::vector<int>::const_reverse_iterator>::value; }
//Returns true
bool g() { return is_iterator<int*>::value; }
//Returns true
bool gc() { return is_iterator<const int*>::value; }
//Returns false
bool h() { return is_iterator<int>::value; }
//Returns false
bool i() { return is_iterator<Foo>::value; }
This implementation uses SFINAE and overloading precedence. test(U&&)
always has higher precedence than test(...)
so it will always be chosen if not removed by SFINAE.
For an iterator type T
, std::iterator_traits<T>
has all of the above mentioned typedefs present so test(U&&)
and test(...)
are both overload candidates. Since test(U&&)
has higher precedence, its always chosen.
For a non-iterator type T
, test(U&&)
fails SFINAE because std::iterator_traits<T>
does not have the nested typedefs. Therefore the only remaining candidate is test(...)
.
Note that this trait will also fail if someone specializes std::iterator_traits<T>
for some type T
and does not provide all of the required typedefs.
Upvotes: 4
Reputation: 21900
I implemented this one some time ago:
template <typename T>
struct is_iterator {
template <typename U>
static char test(typename std::iterator_traits<U>::pointer* x);
template <typename U>
static long test(U* x);
static const bool value = sizeof(test<T>(nullptr)) == 1;
};
It compiles fine using your example. I can't test it on VC though.
Demo here.
Upvotes: 3
Reputation: 11
template < class T, class Enabler = void >
struct is_iterator : public boost::false_type { };
template < class T >
struct is_iterator< T, typename boost::enable_if_c<
sizeof(*(*(T*)0)) + sizeof((*(T*)0)++) + sizeof(++(*(T*)0)) +
sizeof((*(T*)0) == (*(T*)0)) + sizeof((*(T*)0) != (*(T*)0)) +
sizeof((*(T*)0) = (*(T*)0)) >::type > : public boost::true_type { };
Upvotes: 1
Reputation: 26985
template<class T>
struct is_iterator
{
static T makeT();
typedef void * twoptrs[2]; // sizeof(twoptrs) > sizeof(void *)
static twoptrs & test(...); // Common case
template<class R> static typename R::iterator_category * test(R); // Iterator
template<class R> static void * test(R *); // Pointer
static const bool value = sizeof(test(makeT())) == sizeof(void *);
};
Upvotes: 5
Reputation: 133044
Well, you could check for the type to have a nested typedef called iterator_category
This can be done using SFINAE
, and the exact technique can be found in wiki page for SFINAE
. This isn't a 100% method, but all decent iterators should provide the common typedefs for iterators, and the iterator_category is one that is unique to iterators. Also don't forget to check if TYPE is simply a pointer. Pointers are iterators.
Upvotes: 3