如何检查传递的Iterator是一个随机访问迭代器?

Fra*_*ank 30 c++ iterator stl

我有以下代码,它执行一些迭代器算术:

template<class Iterator>
void Foo(Iterator first, Iterator last) {
  typedef typename Iterator::value_type Value;
  std::vector<Value> vec;
  vec.resize(last - first);
  // ...
}
Run Code Online (Sandbox Code Playgroud)

(last - first)表达的作品(据我所知)仅适用于随机访问迭代器(像的那些vectordeque).如何检查传递的迭代器满足此要求的代码?

Jam*_*lis 33

如果Iterator是随机访问迭代器,那么

std::iterator_traits<Iterator>::iterator_category
Run Code Online (Sandbox Code Playgroud)

会的std::random_access_iterator_tag.实现这个的最简洁方法可能是创建第二个函数模板并Foo调用它:

template <typename Iterator>
void FooImpl(Iterator first, Iterator last, std::random_access_iterator_tag) { 
    // ...
}

template <typename Iterator>
void Foo(Iterator first, Iterator last) {
    typedef typename std::iterator_traits<Iterator>::iterator_category category;
    return FooImpl(first, last, category());
}
Run Code Online (Sandbox Code Playgroud)

这样做的好处是FooImpl,如果您愿意,可以为不同类别的迭代器重载.

Scott Meyers在一本有效的C++书籍中讨论了这种技术(我不记得哪一本).

  • hooray用于标签调度. (3认同)

vit*_*aut 13

除了标签调度之外,您还可以将类别与std::random_access_iterator_tag直接使用进行比较std::is_same_v

using category = typename std::iterator_traits<Iterator>::iterator_category;
if constexpr (std::is_same_v<category, std::random_access_iterator_tag>) {
  vec.resize(last - first);
}
Run Code Online (Sandbox Code Playgroud)

这有时会产生更清晰简洁的代码,特别是当您的实现中只有一小部分(例如保留向量大小)依赖于迭代器类别时。

  • 如果非随机访问迭代器会出错,则可以使用“static_assert”。此外,我会考虑 `std::is_convertible_v` 而不是 `std::is_same_v`,因为前者允许某些未来版本的 C++ 具有另一种类型标签的可能性,该标签也是通过继承的 `std::random_access_iterator_tag`。迭代器标签已经使用继承来表达 is-a 关系。 (3认同)