【问题标题】:How std::advance iterator type is deduced?如何推导出 std::advance 迭代器类型?
【发布时间】:2018-06-22 13:31:53
【问题描述】:

请查看std::advance 函数。 根据 cppreference 的复杂性是:

线性。 但是,如果 InputIt 额外满足 RandomAccessIterator 的要求,复杂度是不变的。

那么如果我传递一个迭代器和一个整数,程序如何推断它是什么迭代器以及它是如何实现的?那么该函数有 2 个重载还是什么?

【问题讨论】:

  • std::iterator_traits,见iterator_category
  • 对于这类查询,打开标准库的源代码并尝试通过阅读代码来理解它总是值得的。在 linux 上,它通常可以在 /usr/include/c++/<version> 下找到。
  • 在windods上,@PaulRooney?
  • 不确定抱歉,我手边没有窗户。不过搜索起来并不难。

标签: c++ stl iterator time-complexity


【解决方案1】:

程序如何推断它是什么迭代器以及它是如何实现的?

它使用std::iterator_traits<T>::iterator_category 来确定迭代器类别,然后进行标签调度

std::iterator_traits 专门用于原始指针。原始指针是随机访问迭代器。

例如:

template<class I>
void doadvance(I& i, size_t n, std::random_access_iterator_tag) {
    i += n;
}

template<class I, class Tag>
void doadvance(I& i, size_t n, Tag) {
    while(n--)
        ++i;
}

template<class I>
void advance(I& i, size_t n) {
    using Tag = typename std::iterator_traits<I>::iterator_category;
    doadvance(i, n, Tag{});
}

在 C++17 中,它可以使用 if constexpr 而不是 标记调度,但这会使标准库不向后兼容。这就是为什么它必须继续使用标签调度

IMO,std::next 的接口比std::advance 更好,因为它返回迭代器,因此调用不必占用自己的代码行。

【讨论】:

  • @EduardRostomyan std::iterator_traits 专门用于原始指针。原始指针是随机访问迭代器。
  • @MaximEgorushkin:标准中没有要求它使用这样的标签调度。仍然使用它的 C++17 实现可以选择这样做。现在是的,if constexpr 表达式必须基于随机访问标记,但它不必通过它委托调用。
  • @NicolBolas 我没有说标准要求使用标签调度。我并没有说if constexpr 必须委托。恰恰相反,if constexpr 消除了进行标签调度的需要。
【解决方案2】:

程序如何推断它是什么迭代器

使用std::iterator_traits,即检查std::iterator_traits&lt;It&gt;::iterator_category,如果是std::random_access_iterator_tag,则表示它是一个RandomAccessIterator。

顺便说一句:std::iterator_traits 专门用于原始指针;它们总是 RandomAccessIterator。

那么函数有 2 个重载还是什么?

没有std::advance的重载,实现通常会实现几个辅助函数重载,根据std::advance中的iterator_category调用。

【讨论】:

  • @Slava 答案已修改。
猜你喜欢
  • 1970-01-01
  • 2021-01-03
  • 2019-06-12
  • 2010-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-06
相关资源
最近更新 更多