如何推导出 std::advance 迭代器类型?

Posted

技术标签:

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

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

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

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

【问题讨论】:

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

以上是关于如何推导出 std::advance 迭代器类型?的主要内容,如果未能解决你的问题,请参考以下文章

std::advance - 仅在调试时偏移超出范围失败

哪个更好 std::prev(itr) 或 --itr?

Typescript 推导出泛型成员函数的返回类型

如何使用 JMX 导出器将 JMX 指标从 Kafka 消费者推送到普罗米修斯

python的生成器与迭代器

如何在C ++中推导出函数参数类型?