如何对迭代器的模板特化排序?双向与前向迭代器partition特化顺序影响结果吗?
嘿,我来帮你把这个问题掰扯清楚~
首先先澄清一个小细节:你这里写的其实是函数模板重载,不是模板特化哦——因为两个函数的第三个参数类型(std::bidirectional_iterator_tag 和 std::forward_iterator_tag)是不同的,属于不同的模板参数列表,所以是重载关系,不是特化。不过这不影响核心问题的解答。
一、声明顺序会不会影响执行结果?
完全不会。
C++的重载决议机制是基于「实参与形参的匹配程度」来选择最佳函数的,和声明顺序无关。迭代器标签的继承关系是:input_iterator_tag → forward_iterator_tag → bidirectional_iterator_tag → random_access_iterator_tag。当你传入一个双向迭代器时,编译器会推导它的迭代器类别是bidirectional_iterator_tag,这个类型是forward_iterator_tag的派生类,但重载决议会优先选择最具体的匹配项——也就是接受bidirectional_iterator_tag的那个重载版本,不会因为声明顺序而选错。
只有当两个重载的匹配度完全相同时,声明顺序才会起作用,但你的这两个重载显然不存在这种情况。
二、这两个版本应该存在差异吗?
必须存在差异,而且差异是为了适配不同迭代器的能力,实现最优性能。
前向迭代器(Forward Iterator)只有单向移动的能力(只能用++),而双向迭代器(Bidirectional Iterator)支持双向移动(++和--),这直接决定了两个版本的算法实现逻辑不同:
双向迭代器版本的典型实现
可以利用双向移动的特性,从序列两端往中间遍历,把满足谓词的元素移到前端,不满足的移到后端,交换次数更少,效率更高:
template<class BIter, class UnaryPredicate> BIter __partition(BIter first, BIter last, UnaryPredicate pred, std::bidirectional_iterator_tag) { while (first != last) { // 从左找第一个不满足pred的元素 while (first != last && pred(*first)) ++first; if (first == last) break; // 从右找第一个满足pred的元素 --last; while (first != last && !pred(*last)) --last; if (first == last) break; // 交换两者位置 std::iter_swap(first, last); ++first; } return first; }
前向迭代器版本的典型实现
因为只能单向移动,通常会用一个「慢指针」记录可放置满足谓词元素的位置,遍历整个序列时把符合条件的元素交换到慢指针位置,再移动慢指针:
template<typename FIter, typename UnaryPredicate> FIter __partition(FIter first, FIter last, UnaryPredicate pred, std::forward_iterator_tag) { // 先找到第一个不满足pred的元素,作为交换起点 first = std::find_if_not(first, last, pred); if (first == last) return first; // 遍历后续元素,把满足pred的交换到前面 for (FIter it = std::next(first); it != last; ++it) { if (pred(*it)) { std::iter_swap(first, it); ++first; } } return first; }
这两个版本的时间复杂度都是O(n),但双向版本的交换次数通常更少,空间复杂度都是O(1),充分利用了各自迭代器的特性。
内容的提问来源于stack exchange,提问作者Moises Rojo

