如何检查任意类型是否为迭代器?

时间:2012-08-20 05:47:10

标签: c++ iterator

这是this question的完全重复,除了接受的答案是错误的,所以我再次问它:

如何正确检查给定类型T是否为迭代器?

我尝试解决它:

// Assume enable_if and is_same are defined
// (hoping for a solution that works for C++03 too)

template<class T>
class is_iterator
{
    static char (&test(...))[2];
    template<class U>
    static typename std::enable_if<
        !std::is_same<
            typename std::iterator_traits<T>::value_type,
            void
        >::value,
        char
    >::type test(U);
public:
    static bool const value = sizeof(test(0)) == 1;
};

struct Foo { };

int main()
{
    return is_iterator<Foo>::value;
}
Visual C ++上的

碰巧失败了:

  

...\vc\include\xutility(373)
  错误C2039:'iterator_category':不是'Foo'

的成员

因为iterator_traits正在value_type中寻找Foo的定义,其中(显然)不存在。

意识到{C}有可能使用__if_exists,但我正在寻找便携式解决方案。

4 个答案:

答案 0 :(得分:12)

这样的事情怎么样?

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;
};

示例:

#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

答案 1 :(得分:3)

我不久前实现了这个:

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;
};

使用您的示例编译好。我不能在VC上测试它。

演示here

答案 2 :(得分:2)

我认为这应该是一个完整的解决方案。在http://gcc.godbolt.org上试一试,看看测试函数的结果汇编。

array([  1.,   2.])

此实现使用SFINAE和重载优先级。 #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; } 的优先级始终高于test(U&&),因此如果SFINAE没有删除,则始终会选择它。

对于迭代器类型test(...)T包含所有上述类型定义,因此std::iterator_traits<T>test(U&&)都是重载候选者。由于test(...)具有更高的优先级,因此始终选择它。

对于非迭代器类型test(U&&)T因SFINAE失败,因为test(U&&)没有嵌套的typedef。因此,唯一剩下的候选人是std::iterator_traits<T>

请注意,如果某人专门针对某些类型test(...) std::iterator_traits<T>并且未提供所有必需的typedef,则此特征也会失败。

答案 3 :(得分:1)

除了 C++17 的做法之外,没有什么新东西:

#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;

一些测试:

#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>);

工作原理:

  1. 一些背景:
  • std::false_type::value == false
  • std::true_type::value == true
  • std::void_t<X> <=> void 如果 X 是有效类型。否则会导致替换失败
  • is_iterator<X> 被视为 is_iterator<X, void>
  • 如果专业化匹配,它将被使用
  1. 详情:

如果 T 是迭代器,则存在以下类型:

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

所以 std::void_t<...>void

特化匹配is_iterator<T,void>(还有is_iterator<T>)并继承std::true_type

如果 Tnot 迭代器,那么前一种类型中至少有一个不存在,所以 std::void_t<...> 不命名类型,整个特化是替代失败。所以 is_iterator 的唯一匹配是继承 std::false_type

的默认情况