尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C++ STL中的常用遍历算法分享
前言提到「遍历容器」很多人第一反应是范围 for 循环或者下标循环。这当然没错但 STL 在algorithm和numeric里提供了一组表达力更强的算法std::for_each、std::transform、std::copy_if、std::count_if、std::any_of、std::accumulate等。它们的价值不只是「少写几行循环」而是把「遍历 做什么」拆成两个独立的部分让意图更清楚也更容易组合和复用。常见的误解有四个。第一认为算法一定比手写循环快——是否更快取决于是否内联、是否发生额外拷贝、数据是否命中缓存不能一概而论应该测量而不是猜。第二认为std::remove_if会删除元素——它只是把要保留的元素前移并返回新的逻辑末尾容器大小不变必须配合erase。第三认为std::accumulate的初始值类型无关紧要——初始值决定了累加过程中使用的类型用0累加double会在每一步做整数运算。第四认为for_each里可以随意修改容器结构——遍历期间增删元素会导致迭代器失效属于未定义行为。本文按「遍历变换」「查找统计」「归约删除」三类介绍常用算法然后讨论执行策略与性能注意事项。代码以 C17 为基准涉及并行算法的地方会注明标准版本和实现支持差异。本机没有 C 工具链所有示例只做标准语义推演。一、遍历与变换for_each、transform、copystd::for_each(first, last, f)对区间内每个元素调用f返回传入的可调用对象C11 起。它的典型用途是「对每个元素做一件有副作用的事」例如打印、累加到外部变量、调用成员函数。f可以按值接收元素也可以按引用接收如果希望修改元素参数应写成T// 适用于 C17#include algorithm#include iostream#include vectorint main() {std::vectorint values{1, 2, 3, 4, 5};std::for_each(values.begin(), values.end(), [](int x) {x * 2; // 通过引用修改元素});std::for_each(values.begin(), values.end(), [](int x) {std::cout x ;});std::cout \n;}std::transform则是「把每个元素映射成新值」。一元版本接收输入区间、输出起始迭代器和变换函数返回输出区间末尾的后一个位置二元版本从两个输入区间各取一个元素做运算。输出可以写入另一个容器也可以写入std::back_inserter追加// 适用于 C17#include algorithm#include cstddef#include iterator#include string#include vectorint main() {std::vectorint values{1, 2, 3, 4, 5};std::vectorint squares;squares.reserve(values.size());std::transform(values.begin(), values.end(),std::back_inserter(squares),[](int x) { return x * x; });std::vectorstd::string names{alpha, beta};std::vectorstd::size_t lengths;lengths.reserve(names.size());std::transform(names.begin(), names.end(),std::back_inserter(lengths),[](const std::string s) { return s.size(); });}std::copy和std::copy_if负责复制。copy复制整个区间copy_if只复制满足谓词的元素。两者都返回输出区间中最后一个被写入元素之后的位置便于继续链式写入。使用back_inserter时目标容器会自动扩容如果目标容器已经预留了足够空间也可以直接传dest.begin()。// 适用于 C17#include algorithm#include iterator#include vectorint main() {std::vectorint source{1, 2, 3, 4, 5, 6};std::vectorint evens;std::copy_if(source.begin(), source.end(),std::back_inserter(evens),[](int x) { return x % 2 0; });std::vectorint copied;copied.resize(source.size());auto end std::copy(source.begin(), source.end(), copied.begin());copied.erase(end, copied.end()); // 按实际复制数量收缩}需要特别注意重叠区间std::copy要求目标起点不在源区间内否则行为未定义目标在源之后往右复制时应使用std::copy_backward或者干脆先复制到临时容器。transform也不要把输出写回与输入重叠的位置除非你非常清楚该算法的要求稳妥做法是使用独立的目标容器。二、查找、计数与判定查找类算法的共同点是「找到就停」因此比手写完整遍历更早结束。std::find按值查找std::find_if按谓词查找找不到时返回last。拿到返回值后必须先与end()比较再解引用// 适用于 C17#include algorithm#include iostream#include vectorint main() {std::vectorint values{3, 7, 11, 14, 18};auto it std::find_if(values.begin(), values.end(),[](int x) { return x % 2 0; });if (it ! values.end()) {std::cout first even: *it \n;} else {std::cout not found\n;}}计数与判定算法包括std::count(first, last, value)统计等于某值的元素个数。std::count_if(first, last, pred)统计满足谓词的元素个数。std::any_of(first, last, pred)是否存在满足谓词的元素。std::all_of(first, last, pred)是否所有元素都满足谓词空区间返回true。std::none_of(first, last, pred)是否没有元素满足谓词空区间返回true。这些算法都只遍历一次any_of和none_of在找到反例后立即返回适合做前置条件检查。注意空区间的语义all_of和none_of都返回true这是「真空真」的约定写业务判断时要考虑空集合是否符合预期。// 适用于 C17#include algorithm#include vectorint main() {std::vectorint values{2, 4, 6, 8};bool all_even std::all_of(values.begin(), values.end(),[](int x) { return x % 2 0; });bool has_negative std::any_of(values.begin(), values.end(),[](int x) { return x 0; });auto even_count std::count_if(values.begin(), values.end(),[](int x) { return x % 2 0; });// all_even 为 truehas_negative 为 falseeven_count 为 4}如果区间已经排序查找可以改用std::lower_bound、std::upper_bound或std::binary_search复杂度从线性降到对数。但前提是排序确实成立对无序区间使用二分查找结果没有意义。std::find本身不要求有序适用范围更广。三、归约与删除accumulate、remove_ifstd::accumulate定义在numeric中做的是左折叠从初始值开始依次把每个元素与当前结果交给二元操作。默认操作是也可以传入自定义函数// 适用于 C17#include numeric#include string#include vectorint main() {std::vectorint values{1, 2, 3, 4, 5};int sum std::accumulate(values.begin(), values.end(), 0);double avg std::accumulate(values.begin(), values.end(), 0.0)/ static_castdouble(values.size());std::vectorstd::string words{a, b, c};std::string joined std::accumulate(words.begin(), words.end(), std::string{},[](const std::string acc, const std::string w) {return acc w;});}初始值0的类型是int因此第一个例子的累加在int中进行第二个例子用0.0累加在double中进行。如果把0用于double容器每一步都会把中间结果转回int小数部分被截断结果与预期不同。这是accumulate最常见的坑。字符串拼接则要用std::string{}作为初始值不能传否则模板参数会推导成const char*的语义完全不对。std::remove和std::remove_if的名字容易误导它们不删除元素也不改变容器大小只是把不需要删除的元素依次前移并返回新的逻辑末尾。要真正缩短容器必须使用 erase-remove 惯用法// 适用于 C17#include algorithm#include vectorint main() {std::vectorint values{1, 2, 3, 4, 5, 6};values.erase(std::remove_if(values.begin(), values.end(),[](int x) { return x % 2 0; }),values.end());}remove_if返回的迭代器指向最后一个保留元素之后的位置erase从该位置删到end()。对std::vector来说删除尾部元素的代价与删除数量相关对std::list或std::forward_list应优先使用容器自己的remove/remove_if成员函数它们直接调整链表指针不需要先搬运元素。适用于所有序列容器的erase_if直到 C20 才加入标准std::erase_ifC17 及以前仍需手写 erase-remove。若使用 C20可以直接写std::erase_if(values, pred);但需要确认标准库实现已支持。四、执行策略与性能注意C17 为许多算法增加了执行策略重载定义在execution中// 适用于 C17并行算法需标准库支持#include algorithm#include execution#include vectorint main() {std::vectorint values{1, 2, 3, 4, 5};std::for_each(std::execution::par,values.begin(), values.end(),[](int x) { x 1; });}并行算法是 C17 引入的但很多标准库实现到较晚版本才提供且通常需要额外链接并行运行时例如 GCC 的 libstdc 需要 TBB。使用前应确认所用标准库版本与链接配置。使用执行策略时要注意传给算法的可调用对象可能被多个线程同时调用不能有无保护的共享可变状态。标准执行策略下若元素访问函数抛出异常标准要求调用std::terminate异常不会正常传播。并行版本不保证执行顺序for_each的并行重载返回void不能依赖返回值。对小容器线程调度开销可能超过收益是否启用应以测量为准。不使用执行策略时算法通常是顺序执行的。std::for_each按从头到尾的顺序应用函数std::accumulate严格从左到右折叠std::reduceC17则允许任意顺序合并因此要求操作满足结合律和交换律浮点加法在严格意义上不满足结合律使用时要想清楚。性能方面算法本身通常会被内联展开成与手写循环相近的代码尤其是 lambda 作为模板参数传入时。真正的开销往往来自输出容器没有预留空间导致反复扩容、按值传递大对象产生拷贝、谓词里有隐藏的分配或锁、以及缓存不友好地跳跃访问。优化应从这些地方入手并用std::chrono::steady_clock或性能分析工具测量不要凭「算法一定更快」的直觉下结论。常见坑点❌ 调用std::remove_if后就认为元素已经删除。✅ 它只返回新的逻辑末尾必须再erase(new_end, end())才真正缩短容器。❌ 用std::accumulate(v.begin(), v.end(), 0)累加double。✅ 初始值决定累加类型应写0.0或T{}避免中间结果被截断。❌ 字符串拼接时把作为accumulate的初始值。✅ 用std::string{}否则模板参数会推导为const char*。❌ 在for_each的回调里push_back或erase当前容器。✅ 遍历期间改变容器结构会使迭代器失效先收集操作遍历结束后再修改。❌ 对无序区间使用std::binary_search。✅ 二分查找要求区间已按相同比较规则排序否则结果无意义。❌ 解引用find的返回值前不检查是否等于end()。✅ 找不到时返回last解引用end()是未定义行为。❌ 在copy中让目标区间与源区间重叠。✅ 目标起点不能在源区间内目标在源之后时用copy_backward或先复制到临时容器。❌ 认为并行算法一定更快或在线程间共享无保护的计数器。✅ 小数据量下调度开销可能更大并行回调必须避免数据竞争。总结需求常用算法头文件关键点对每个元素执行操作for_each、for_each_nalgorithm按引用接收才能修改映射为新值transformalgorithm返回输出区间末尾按条件复制copy_ifalgorithm配合back_inserter追加查找find、find_ifalgorithm找不到返回last统计/判定count_if、any_of、all_ofalgorithm空区间语义要留意归约accumulate、reducenumeric初始值类型决定运算类型条件删除remove_iferasealgorithm单独remove_if不改变大小STL 遍历算法的核心价值是「用名字表达意图」读者看到count_if就知道是在统计看到transform就知道是在映射不必逐行分析循环体。使用时要记住三条底线删除必须配erase、归约的初始值决定类型、遍历期间不要改变容器结构。把这三条守住算法就能成为比手写循环更清晰、也更容易维护的工具。
RELATED

相关推荐

浏览器原生网络感知:NetworkInformation API 实战与降级策略

浏览器原生网络感知:NetworkInformation API 实战与降级策略

1. 被忽视的浏览器原生能力:NetworkInformation API 到底能做什么 第一次接触 NetworkInformation 这个 API 是在做一个移动端视频播放器的时候。产品经理提了一个需求:用户在地铁里、电梯里这种弱网环境下,视频要自动降码率,别…

📅 2026/10/21 22:30:27
用插件思维重构知识工作:从采集到沉淀的高效流水线设计

用插件思维重构知识工作:从采集到沉淀的高效流水线设计

1. 我为什么用“插件思维”重构知识工作 先说说我遇到的真实情况。知识工作者的日常,拆开来看无非是这么几件事:读资料、记想法、写文档、回消息、整理信息。听起来简单,但真做起来你会发现,时间全浪费在来回切换上了。浏览器开十…

📅 2026/10/21 22:30:27
Rust边缘进程管理器REA:从设计到落地的实战指南

Rust边缘进程管理器REA:从设计到落地的实战指南

REA这几个字母,看起来像某个没什么含义的内部代号。当时我也只是顺手给手里这个用Rust写的边缘进程管理器起了个名字,Rust Edge Agent,简写REA。没想到它后来从一个小工具,长成了我在多个边缘项目里都会优先部署的底座。这篇文章想…

📅 2026/10/21 22:30:27
MORE NEWS

更多资讯

📰

ECE R13-H与I线联合建模:纯电动车能量回收策略落地核心

简介:本资源是一篇发表于《武汉理工大学学报(交通科学与工程版)》的学术论文,面向新能源汽车领域研究人员、车辆工程专业师生及电控系统工程师,聚焦纯电动汽车制动能量回收这一核心续航瓶颈问题。论文基于ECE-R13法规限…

📰

DNS主从复制与分离解析实战:高可用架构配置与排错指南

1. 从一个真实运维场景说起:为什么单台DNS服务器迟早会出事很多团队在项目初期为了图省事,往往只部署一台DNS服务器,所有内网域名解析、外部域名转发都压在这一台机器上。刚开始几十台机器的时候确实没什么问题,查询量小、配置简单…

📰

Docker Desktop 实用指南:从镜像管理到数据卷、网络与清理

工欲善其事必先利其器,这话放在 Docker Desktop 上特别接地气。上一回我们把环境跑起来,基础命令也能凑合用了,但真正到了项目开发里,光“能跑”远远不够。镜像怎么拉、卷怎么挂、网络怎么通、磁盘怎么清,这些问题不顺…

📰

AI技能包方法论:把提示词沉淀为可复用的工作流

二十多套“一次性”提示词用下来,我发现自己一直在重复造轮子。今天要说的不是某个具体框架,而是围绕 "skills" 这个词展开的一套方法论——把高频任务沉淀成可复用、可版本化、可共享的技能包。先说清楚它是什么:skills 不是一条提…

📰

知识工作插件全解析:从信息捕获到自动化,打造高效知识工作流

知识工作者最浪费时间的事情,往往不是工作任务本身太难,而是被大量低层次的"信息搬运"打断——从一个窗口切到另一个窗口,把一段文字复制三遍再改格式,翻找一份几天前存过的资料,为了一个术语在几个工具之间…

📰

卡车域控制器系统框图深度解析:供电、总线与地线设计实战指南

简介:本资源是一份面向汽车电子工程师、智能网联车辆开发者及高校相关专业研究者的卡车域控制器系统架构图解资料,聚焦商用车电子电气系统集成设计与通信协议落地实践。文件为单页PDF(77KB),完整呈现了以SPC58ECXX微控…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬