【问题标题】:How to define is_iterator type trait?如何定义 is_iterator 类型特征?
【发布时间】:2014-10-07 01:36:04
【问题描述】:
  • 我正在尝试编写 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 {};

LIVE DEMO


问:有没有比上面显示的更合适的方法来定义is_iterator 类型特征?

【问题讨论】:

  • 为什么这需要是一个完整的类或结构?为什么没有bool is_iterator(T)?
  • @Josh 在 SFINAE 中使用。
  • 您可以检查迭代器概念的所有要求:en.cppreference.com/w/cpp/concept/Iterator。
  • 注: pre-DR 2408 iterator_traits 对 SFINAE 不友好,将其实例化为非迭代器类型是一个硬错误,而不是替换失败
  • 一个类型是一个迭代器,如果它是一个输入迭代器或一个输出迭代器。定义is_input_iterator&lt;T&gt; 和is_output_iterator&lt;T&gt; 留给读者作为练习。 :-)

标签: c++ c++11 iterator typetraits c++14


【解决方案1】:

正如我在 cmets 中所说,这里介绍的解决方案在某些实现中依赖于 iterators_traits 的不可移植属性。根据 C++03 和 C++11 标准,iterator_traits 仅为迭代器(以及指针的特殊情况)定义,因此任何其他用途都是未定义的行为。具体来说,不能保证在 SFINAE 上下文中使用iterator_traits&lt;T&gt;::pointer,因为实例化iterator_traits&lt;T&gt; 将引用T::value_type、T::pointer、T::iterator_category 等,而这发生在 SFINAE 没有的“即时上下文”之外'不申请。

C++14 将解决这个问题 应该解决这个问题(它发生在 C++14 之后,DR 2408),但对于 C++11,定义 @987654331 的安全方式@ 是编写一个特征来检查迭代器必须定义的所有必需操作。所有迭代器都需要支持的唯一操作是operator* 以及前后增量。不幸的是,有些类型定义的操作不是有效的迭代器,因此编写正确的 trait 非常困难。

【讨论】:

  • Jonathan,您的回答是否也适用于this question 的命题?还是其中之一是足够好的“检查所有必需操作的特征”?此外,如果一个类型满足迭代器概念的所有要求(据说可以检查) - 它不是事实上的迭代器,尽管是偶然的吗?
  • 这些答案中的大多数都是错误的(因为他们假设迭代器将具有特定的嵌套 typedef),但 Barry 的答案可能已经足够好了。是的,如果一个类型满足某个概念的所有要求,那么它就是该概念的模型。
  • @JonathanWakely 很抱歉提出一个老问题。你说“巴里的回答可能已经足够好了”。不幸的是,我没有看到巴里。您是否参考了带有时间戳的答案 2014 年 8 月 13 日 16:26(当前由用户名 Praetorian)?
  • @C.Binair 我正在回复关于另一个问题的评论,所以我在谈论那个问题的答案,即this answer.
【解决方案2】:

如果std::iterator_traits&lt;T&gt;::pointer 不是指针类型,则检查失败,例如T = std::ostream_iterator&lt;U&gt;。

我认为更好的测试可能是std::iterator_traits&lt;T&gt;::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 {};

【讨论】:

  • 其他迭代器标签呢?随机、双向等
  • @IonTodirel 所有输入迭代器类别均源自input_iterator_tag,参见this
【解决方案3】:

我认为没有必要检查iterator_traits 的嵌套类型定义的任何特定属性。仅仅检查iterator_traits&lt;T&gt;::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 指出)。

【讨论】:

  • 但是 iterator_traits 在 C++11 中对 SFINAE 不友好,因此为非迭代器类型实例化 iterator_traits&lt;X&gt; 是未定义的行为,并且错误不在直接上下文中,因此是错误不是替换失败(cplusplus.github.io/LWG/lwg-active.html#2408 地址)
  • 幸运的是,一些实现多年来一直在提供对 SFINAE 友好的iterator_traits,因为这样更有用:)
  • 许多几乎 SFINAE iterator_traits 将在 iterator_traits&lt;void*&gt;(和 cv 变体)上严重失败。 (T* 特化会捕获它,但实例将无法编译)
  • 可悲的是,void const* void volatile* void const volatile* 可能也会中断。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-18
  • 2015-01-21
  • 2013-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多