簡體   English   中英

如何檢查模板參數是否為迭代器類型?

[英]How to check if a template parameter is an iterator type or not?

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個assert語句通過?

這樣的事情怎么樣?

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);
   static_assert(is_iterator<int*>::value);
   static_assert(is_iterator<std::vector<int>::iterator>::value);
}

http://liveworkspace.org/code/7dcf96c97fd0b7a69f12658fc7b2693e

幾年后來到這里,C ++ 11和C ++ 14使這些事情變得容易多了。 迭代器的核心是不可改變的,可遞增的。 如果它是一個輸入迭代器 ,那么也可以比較。 讓我們選擇后者 - 因為這看起來像你想要的。

最簡單的版本是使用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]要求:

如果IteratorIterator的類型,那么它是必需的

 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不是迭代器。

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

我相信這應該是一個完整的解決方案。 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,則此特征也會失敗。

我前段時間實現了這個:

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 上測試它。

演示 在這里

那么,您可以檢查類型是否具有嵌套的typedef,稱為iterator_category這可以使用SFINAE完成,並且可以在SFINAE wiki頁面中找到確切的技術。 這不是一個100%的方法,但是所有體面的迭代器都應該為迭代器提供公共的typedef,而iterator_category是迭代器獨有的。 另外不要忘記檢查TYPE是否只是一個指針。 指針是迭代器。

沒有什么新鮮事,但 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 is_iterator默認情況

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

原始海報澄清說他們實際上是在尋找一種識別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類中提供它們,這對於使用裸指針作為迭代器是必需的,並且需要標准庫實現。)

從 C++20 開始:

檢測任何迭代器的正確方法是使用概念,即std::input_or_output_iterator

引用cppreference

 template <class I> concept input_or_output_iterator = requires(I i) { { *i } -> /*can-reference*/; } && std::weakly_incrementable<I>;

input_or_output_iterator 概念 forms 迭代器概念分類的基礎; 每個迭代器類型都滿足 input_or_output_iterator 要求。

請注意,exposition only type /*can-reference*/實際上只是意味着不是void ,並且可以像下面這樣完成:

template <class T>
using with_ref_t = T &;

template <class T>
concept can_reference = requires() { typename detail::with_ref_t<T>; };

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM