我正在尝试编码is_iterator<T>
类型特征。何时T
是迭代器类型is_iterator<T>::value == true
,否则为is_iterator<T>::value == false
。
到目前为止我尝试了什么:
template <class, class Enable = void>
struct is_iterator : std::false_type {};
template <typename T>
struct is_iterator<T, typename std::enable_if<std::is_pointer<typename
std::iterator_traits<T>::pointer>::value>::type> : std::true_type {};
问:是否有更合适的方式来定义is_iterator
类型特征而不是上面显示的特征?
答案 0 :(得分:6)
正如我在评论中所说,这里介绍的解决方案在某些实现中依赖于iterators_traits
的非可移植属性。根据C ++ 03和C ++ 11标准,iterator_traits
仅为迭代器(以及指针的特殊情况)定义,因此任何其他用途都是未定义的行为。具体来说,在SFINAE上下文中使用iterator_traits<T>::pointer
并不能保证有效,因为实例化iterator_traits<T>
会引用T::value_type
,T::pointer
,T::iterator_category
等等。发生在SFINAE不适用的“直接背景”之外。
C ++ 14 将修复应该解决的问题(它发生在C ++ 14之后DR 2408),但对于C ++ 11来说,安全的定义方式is_iterator
是编写一个特征,用于检查迭代器必须定义的所有必需操作。所有迭代器都需要支持的唯一操作是operator*
以及前后增量。不幸的是,可能存在定义那些不是有效迭代器的操作的类型,因此编写正确的特征非常困难。
答案 1 :(得分:3)
如果std::iterator_traits<T>::pointer
是不是指针的类型,则检查失败,例如T = std::ostream_iterator<U>
。
我认为更好的测试可能是std::iterator_traits<T>::iterator_category
是std::input_iterator_tag
还是派生类型,还是std::output_iterator_tag
。
template <class, class Enable = void> struct is_iterator : std::false_type {};
template <typename T>
struct is_iterator
<T,
typename std::enable_if<
std::is_base_of<std::input_iterator_tag, typename std::iterator_traits<T>::iterator_category>::value ||
std::is_same<std::output_iterator_tag, typename std::iterator_traits<T>::iterator_category>::value
>::type>
: std::true_type {};
答案 2 :(得分:3)
我认为没有必要检查iterator_traits
的嵌套typedef的任何特定属性。它应该足以检查是否存在iterator_traits<T>::value_type
(或任何其他嵌套的typedef),因为每个迭代器都有一个。
#include <type_traits>
#include <iostream>
#include <iterator>
template<typename>
struct void_ {
typedef void type;
};
// remove typename spam below:
template<typename Discard>
using void_t=typename void_<Discard>::type;
template<typename T>
using decay_t=typename std::decay<T>::type;
// stick helper types into details, so the interface
// for is_iterator is cleaner:
namespace details {
template<typename T, typename Enable=void>
sturct is_iterator : is_iterator2<T, Enable> {};
// special case: void* is not an iterator
// but T* specialization would pick it up
// if there weren't the following:
template<typename V>
struct is_iterator<V*, decay_t<V>> : std::false_type {};
// phase 2: SFINAE pass to std::iterator_traits test
// valid in C++14, and in many C++11 compilers, except
// for above void issue:
template<typename, typename Enable = void>
struct is_iterator2 : std::false_type {};
template<typename T>
struct is_iterator2<T,
void_t< typename std::iterator_traits<T>::value_type>
> : std::true_type {};
}
template<typename T>
struct is_iterator : details::is_iterator<T> {};
int main()
{
std::cout
<< is_iterator<int*>::value
<< is_iterator<double>::value;
}
不幸的是,这不能保证在C ++ 11中有效,但是C ++ 14会修复它(感谢Jonathan Wakely指出它)。