在学习C++ STL库时,有时候我会去看一下其中的一些函数或者方法是如何实现的,在这个过程中不时会碰到一些个人认为写得比较精彩或者洗练简洁的源代码。故即日起专门开一个文章用作笔记进行摘抄学习。本笔记不定期更新。
由于本人接触C++尚不是很久,且本笔记主要是供个人学习之用,其中的注释如有错误偏差,还请读者批评指正。
本文所摘抄的源码均来自于Microsoft开源的MSVC STL库:github.com/microsoft/S…
1. std::unique的实现代码
关于std::unique的使用示例如下:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int arr[] = {1, 1, 4, 4, 5};
vector<int> s(arr, arr + 5);
// std::unique用于对已排序的单调序列容器进行去重
// 该函数会返回一个去重后序列的右开区间迭代器
auto newEnd = std::unique(s.begin(), s.end());
// 输出1 4 5
copy(s.begin(), newEnd, ostream_iterator<int>(cout, "\n"));
}
std::unique实现关键源码片段如下:
template <class _FwdIt, class _Pr>
inline _FwdIt unique(_FwdIt _First, _FwdIt _Last, _Pr _Pred) {
// remove each satisfying _Pred with previous
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_First);
const auto _ULast = _Get_unwrapped(_Last);
if (_UFirst != _ULast) {
// 去重过程中维护两个指针:
// _UFirstb指向序列中现在正在被检查是否存在重复的元素
// _UFirst始终位于_UFirstb右侧,指向准备检查序列中是否与_UFirstb重复的元素
// 外层循环用于控制暂时没找到重复元素的前提下,_UFirstb和_UFirst指针的向右移动
for (auto _UFirstb = _UFirst; ++_UFirst != _ULast; _UFirstb = _UFirst) {
// 如果发现_UFirstb和_UFirst指向的元素重复
if (_Pred(*_UFirstb, *_UFirst)) {
// 将_UFirst向右滑动,直到找到第一个与_UFirstb指向元素不再重复的元素
while (++_UFirst != _ULast) {
if (!_Pred(*_UFirstb, *_UFirst)) {
// 找到该元素后,将_UFirstb向右滑动一位,将找到的元素拷贝到该处。
// 当下一次循环执行的时候,_UFirstb自然而然就指向了新的待检查的元素,
// 依靠循环条件++_UFirst != _ULast能够保证_UFirst再一次定位到了_UFirstb的后一位
// 因此可以继续利用这个while循环来实现后续元素的去重检查与元素前移
*++_UFirstb = _STD move(*_UFirst);
}
}
// 这里++UFirstb是为了保证返回的是一个开的右区间
_Seek_wrapped(_Last, ++_UFirstb);
return _Last;
}
}
}
_Seek_wrapped(_Last, _ULast);
return _Last;
}
inline _FwdIt unique(_FwdIt _First, _FwdIt _Last) { // remove each matching previous
return _STD unique(_First, _Last, equal_to<>{});
}