20

这是这个问题的完全重复,除了接受的答案是错误的,所以我再问一遍:

如何正确检查给定类型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_typein的定义Foo,(显然)不存在。

我知道这在 Visual C++ 上__if_exists一种可能性,但我正在寻找一种可移植的解决方案。

4

4 回答 4

21

这样的事情怎么样?

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

于 2012-08-20T06:08:41.543 回答
5

我相信这应该是一个完整的解决方案。在http://gcc.godbolt.org上试用它并查看测试功能的生成程序集。

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

此实现使用 SFINAE 和重载优先级。test(U&&)始终具有更高的优先级,test(...)因此如果未被 SFINAE 删除,它将始终被选中。

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

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

std::iterator_traits<T>请注意,如果有人专门研究某种类型T并且不提供所有必需的 typedef ,则此 trait 也会失败。

于 2015-06-10T20:10:50.870 回答
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 上测试它。

演示在这里

于 2012-08-20T06:19:18.360 回答
2

除了 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

如果T不是迭代器,则至少有一个先前的类型不存在,因此std::void_t<...>命名类型并且整个特化是替换失败。所以唯一匹配的is_iterator是继承的默认情况std::false_type

于 2021-06-03T13:15:37.427 回答