如何推导出 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
更好,因为它返回迭代器,因此调用不必占用自己的代码行。
【讨论】:
@EduardRostomyanstd::iterator_traits
专门用于原始指针。原始指针是随机访问迭代器。
@MaximEgorushkin:标准中没有要求它使用这样的标签调度。仍然使用它的 C++17 实现可以选择这样做。现在是的,if constexpr
表达式必须基于随机访问标记,但它不必通过它委托调用。
@NicolBolas 我没有说标准要求使用标签调度。我并没有说if constexpr
必须委托。恰恰相反,if constexpr
消除了进行标签调度的需要。【参考方案2】:
程序如何推断它是什么迭代器
使用std::iterator_traits
,即检查std::iterator_traits<It>::iterator_category
,如果是std::random_access_iterator_tag
,则表示它是一个RandomAccessIterator。
顺便说一句:std::iterator_traits
专门用于原始指针;它们总是 RandomAccessIterator。
那么函数有 2 个重载还是什么?
没有std::advance
的重载,实现通常会实现几个辅助函数重载,根据std::advance
中的iterator_category调用。
【讨论】:
@Slava 答案已修改。以上是关于如何推导出 std::advance 迭代器类型?的主要内容,如果未能解决你的问题,请参考以下文章