这是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
,但我正在寻找便携式解决方案。
答案 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>);
工作原理:
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
是 not 迭代器,那么前一种类型中至少有一个不存在,所以 std::void_t<...>
不命名类型,整个特化是替代失败。所以 is_iterator
的唯一匹配是继承 std::false_type