如何检查模板参数是否是迭代器类型?

时间:2010-12-02 14:20:30

标签: c++ templates sfinae

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
}

问题是:如何使五个断言语句通过?

5 个答案:

答案 0 :(得分:11)

几年后来到这里,C ++ 11和C ++ 14使这些事情变得容易多了。 iterator的核心是不可改变的,可递增的。如果它是input iterator,那么也可以比较。让我们选择后者 - 因为这看起来像你想要的。

最简单的版本是使用void_t

template <typename... >
using void_t = void;

基本情况:

template <typename T, typename = void>
struct is_input_iterator : std::false_type { };

有效案例专业化:

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

别名:

template <typename T>
using is_input_iterator_t = typename is_input_iterator<T>::type;

无需依赖iterator_category或使用繁琐的C ++ 03样式检查使用重载决策。表达SFINAE就在它的位置。

正如Wakely先生在评论中指出的那样,[iterator.traits]要求:

  

如果Iterator是类型,则需要   迭代器,类型

iterator_traits<Iterator>::difference_type
iterator_traits<Iterator>::value_type
iterator_traits<Iterator>::iterator_category
     

分别定义为迭代器的差异类型,值类型和迭代器类别。

因此我们可以定义迭代器特征来简单地检查:

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

如果iterator_traits<T>::iterator_category格式不正确,则T不是迭代器。

答案 1 :(得分:6)

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

答案 2 :(得分:2)

好吧,您可以检查类型是否具有名为iterator_category的嵌套typedef这可以使用SFINAE来完成,并且可以在wiki page for SFINAE中找到确切的技术。这不是一个100%的方法,但是所有体面的迭代器都应该为迭代器提供公共的typedef,而iterator_category是迭代器独有的。另外不要忘记检查TYPE是否只是一个指针。指针是迭代器。

答案 3 :(得分:1)

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

答案 4 :(得分:1)

原始海报澄清说他们实际上是在寻找一种识别InputIterator的方法(参见http://en.cppreference.com/w/cpp/concept/InputIterator),因为他们希望能够增加和取消引用迭代器。这在标准C ++ 11中有一个非常简单的SFINAE解决方案,例如类似于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) { /* ... */ };

这依赖于迭代器类型traits类,它定义了Armen Tsirunyan认为迭代器本身需要的typedef。 (迭代器可以提供那些typedef,但它们也可以在traits类中提供它们,这对于使用裸指针作为迭代器是必需的,并且标准库实现需要这样做。)< / p>