You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用STL算法替代C++中需访问索引的手写循环

如何用STL算法替代C++中需访问索引的手写循环

嘿,我完全理解你想遵循《Effective STL》里Item 43的建议,把手写循环替换成STL算法的想法——毕竟算法不仅更简洁,还能避免手动维护索引时容易犯的错误。咱们先拆解你原来的手写循环做了什么,再一步步用STL工具来替代它。

首先看你的using_for_loop函数,核心逻辑分两步:

  1. 先按自定义规则排序vec:优先比较第一个元素,再比较后两个元素的minmax结果。
  2. 遍历排序后的容器,把连续的“等价”元素(第一个元素相同,且后两个元素的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

火山引擎 最新活动