[英]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]要求:
如果
Iterator
是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
不是迭代器。
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 刪除,它將始終被選中。
對於迭代器類型T
, std::iterator_traits<T>
具有上述所有類型定義,因此test(U&&)
和test(...)
都是重載候選者。 由於test(U&&)
具有更高的優先級,因此它總是被選中。
對於非迭代器類型T
, test(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>);
怎么運行的:
std::false_type::value == false
std::true_type::value == true
std::void_t<X> <=> void
如果 X 是有效類型。 如果不是它會導致替換失敗is_iterator<X>
被視為is_iterator<X, void>
如果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類中提供它們,這對於使用裸指針作為迭代器是必需的,並且需要標准庫實現。)
檢測任何迭代器的正確方法是使用概念,即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.