如何用STL算法替代C++中需访问索引的手写循环
如何用STL算法替代C++中需访问索引的手写循环
嘿,我完全理解你想遵循《Effective STL》里Item 43的建议,把手写循环替换成STL算法的想法——毕竟算法不仅更简洁,还能避免手动维护索引时容易犯的错误。咱们先拆解你原来的手写循环做了什么,再一步步用STL工具来替代它。
首先看你的using_for_loop函数,核心逻辑分两步:
- 先按自定义规则排序
vec:优先比较第一个元素,再比较后两个元素的minmax结果。 - 遍历排序后的容器,把连续的“等价”元素(第一个元素相同,且后两个元素的
minmax相等)只保留一个,最后调整容器大小。
其实你的手写去重逻辑,正好是STL中std::unique算法的职责——只不过std::unique默认用==判断等价,我们只需要给它传一个自定义的谓词,就能适配你的等价规则。
直接用std::unique替代手写去重循环
下面是修改后的no_for_loop函数,完全用STL算法实现,不需要手动维护索引:
#include <algorithm> #include <vector> #include <array> void assign_vec(const int64_t &count, std::vector<std::array<int64_t, 3>> &v) { for (int i = 0; i < count;++i) { std::array<int64_t, 3 > a{i,i+1,i+2}; v.emplace_back(a); } } int no_for_loop() { std::vector<std::array<int64_t, 3>> vec; assign_vec(10'000'000, vec); // 第一步:和原函数一致的排序逻辑 std::sort(vec.begin(), vec.end(), [](const auto& a, const auto& b) { if (a[0] != b[0]) return a[0] < b[0]; return std::minmax(a[1], a[2]) < std::minmax(b[1], b[2]); }); // 第二步:用std::unique替代手写去重循环 // 自定义等价判断谓词:两个元素是否属于同一重复组 auto new_end = std::unique(vec.begin(), vec.end(), [](const auto& a, const auto& b) { return a[0] == b[0] && std::minmax(a[1], a[2]) == std::minmax(b[1], b[2]); }); // 移除末尾的重复元素(std::unique只是把重复元素移到末尾,并没有真正删除) vec.erase(new_end, vec.end()); // 也可以用vec.resize(std::distance(vec.begin(), new_end)); 效果完全一致 return 0; }
为什么这个方案可行?
std::unique的工作原理和你的手写循环几乎一模一样:
- 它遍历容器,把每个不与前一个元素等价的元素移到容器的前半部分。
- 最后返回的
new_end迭代器指向去重后的容器末尾,我们只需要用erase或者resize截断容器即可。 - 因为你已经提前按自定义规则排好序了,所有等价的元素都是连续的,这正好满足
std::unique的使用前提(它只处理连续的重复元素)。
关于“需要访问索引”的通用场景
如果以后遇到真的需要在算法中访问元素索引的场景,有几种处理方式:
- 手动维护计数器:在调用算法前定义一个计数器变量,通过引用捕获到lambda中,每次迭代时递增:
size_t idx = 0; std::for_each(vec.begin(), vec.end(), [&idx](const auto& elem) { // 使用idx处理当前元素 idx++; }); - 捕获容器起始迭代器:对于随机访问容器(比如
vector),可以通过元素地址差直接计算索引,或者用std::distance计算迭代器间距:auto begin_it = vec.begin(); std::for_each(vec.begin(), vec.end(), [begin_it](const auto& elem) { size_t idx = &elem - &*begin_it; // 使用idx处理当前元素 }); - C++20及以上用
std::views::enumerate:可以直接遍历元素和索引的配对,代码更直观:for (const auto& [idx, elem] : vec | std::views::enumerate) { // 使用idx和elem处理逻辑 }
不过回到你的当前场景,因为排序后等价元素连续,std::unique完全能满足需求,根本不需要手动处理索引——这也是STL算法的优势:它帮我们封装了底层的遍历逻辑,让我们只需要关注业务规则。
备注:内容来源于stack exchange,提问作者qqqqq




