This is an exact duplicate of this question, except that accepted answer is wrong, so I ask it again:
How do you correctly check to see if
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, "ass");
static_assert(is_iterator<int*>::value, "ass");
static_assert(is_iterator<std::vector<int>::iterator>::value, "ass");
}
http://liveworkspace.org/code/7dcf96c97fd0b7a69f12658fc7b2693e
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.
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.