导读:本期聚焦于零壳创作的《C++ STL中for_each和transform算法怎么用?两者有什么区别?》,敬请观看详情。for_each和transform是C++ STL中最常用的两个遍历算法,但不少人在选型时容易混淆。本文从函数签名入手,详细讲解for_each的单区间遍历用法、返回值特性以及它如何配合lambda表达式简化代码,同时介绍transform的单区间复制、原地修改和双区间合并三种典型场景。文中还会通过完整可编译的代码示例对比两者在返回值、用途上的核心差异,并给出需要operator加法的仿函数写法、迭代器失效等实践中容易踩坑的细节,帮助你写出更符合STL设计习惯的遍历代码。

for_each和transform是C++ STL算法库中使用频率最高的两个遍历工具。前者偏向于“对每个元素执行某个动作”,后者偏向于“把每个元素经过运算后存到另一个地方”。虽然看起来相似,但两者在返回值设计、适用场景上差别很大。本文将分别介绍这两个算法的语法、用法和注意事项,最后给出对比总结。

C++ STL中for_each和transform算法怎么用?两者有什么区别?

for_each的基本用法与返回值特性

for_each定义在<algorithm>头文件中,其函数原型为for_each(InputIt first, InputIt last, UnaryFunction f)。它的作用是对区间[first, last)中的每个元素调用一次f(elem),并最终把这个函数对象按值返回。这一点经常被忽视,却非常有用:如果传入的是一个有状态的仿函数,可以通过返回值拿到遍历过程中累积的状态。

来看一个最简单的例子,遍历vector并打印每个元素:

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v{1, 2, 3, 4, 5};

    // 使用lambda对每个元素执行打印动作
    std::for_each(v.begin(), v.end(), [](int x) {
        std::cout << x << " ";
    });
    std::cout << std::endl;
    return 0;
}

for_each的一个重要特性是它可以返回传入的函数对象。下面的例子利用这一点统计元素总和,同时遍历过程中不需要额外的全局变量:

#include <algorithm>
#include <vector>
#include <iostream>

struct Sum {
    int total = 0;
    void operator()(int x) { total += x; }
};

int main() {
    std::vector<int> v{1, 2, 3, 4, 5};

    // for_each会返回传入的函数对象副本,从中取出累积结果
    Sum s = std::for_each(v.begin(), v.end(), Sum{});
    std::cout << "sum = " << s.total << std::endl; // 输出 15
    return 0;
}

需要注意的是,for_each返回的是函数对象的拷贝,因此要求仿函数的状态在拷贝时能正确保留。另外,从C++11开始,range-based for循环在大多数“只读遍历”场景下比for_each更直观,但for_each在需要传递函数对象、配合STL管道风格代码时依然有自己的优势。

transform的三种典型用法

transform同样位于<algorithm>头文件,它有两种重载形式。第一种是单输入区间版本:transform(InputIt first, InputIt last, OutputIt d_first, UnaryOp op),把每个元素经过op运算后写入目标区间;第二种是双输入区间版本:transform(InputIt first1, InputIt last1, InputIt first2, OutputIt d_first, BinaryOp op),把两个区间中对应位置的元素进行二元运算后写入目标区间。它返回目标区间末尾的迭代器。

第一种典型用法是“转换并复制到另一个容器”,原容器保持不变:

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> src{1, 2, 3, 4};
    std::vector<int> dst(src.size());

    // 每个元素平方后写入dst,src不变
    std::transform(src.begin(), src.end(), dst.begin(),
                   [](int x) { return x * x; });

    for (int x : dst) std::cout << x << " "; // 输出 1 4 9 16
    return 0;
}

第二种是原地修改,让目标迭代器和源迭代器指向同一容器即可,这在批量修改元素时非常方便:

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v{1, 2, 3, 4, 5};

    // 原地将每个元素取负
    std::transform(v.begin(), v.end(), v.begin(),
                   [](int x) { return -x; });

    for (int x : v) std::cout << x << " "; // 输出 -1 -2 -3 -4 -5
    return 0;
}

第三种是双区间合并,比如把两个vector对应位置相加:

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> a{1, 2, 3};
    std::vector<int> b{10, 20, 30};
    std::vector<int> c(a.size());

    std::transform(a.begin(), a.end(), b.begin(), c.begin(),
                   [](int x, int y) { return x + y; });

    for (int x : c) std::cout << x << " "; // 输出 11 22 33
    return 0;
}

使用双区间版本时要特别注意:第二个输入区间只提供了起始迭代器,算法默认它至少和第一个区间一样长,如果实际长度不足会引发未定义行为,编译器和运行时都不会帮你检查。如果目标容器为空,记得先调用resizeback_inserter来保证有足够空间,否则写入也会越界。

两者的核心区别与选型建议

从设计意图上看,for_each改变的是“状态”,它对元素做什么完全由你决定,可以是打印、累计、修改元素本身;transform强调的是“计算并产出新值”,它的函数对象必须有返回值,运算结果会被写入目标区间。因此transform的lambda里一定要写return语句,而for_each的lambda通常返回void。

从返回值上看也有明显差异:for_each返回传入的函数对象本身(按值拷贝),transform返回输出区间的尾后迭代器。利用transform的返回值配合back_inserter,可以优雅地向空容器追加结果:

#include <algorithm>
#include <iterator>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> src{1, 2, 3};
    std::vector<int> dst; // 空容器,无需预先分配空间

    std::transform(src.begin(), src.end(),
                   std::back_inserter(dst),
                   [](int x) { return x * 10; });

    for (int x : dst) std::cout << x << " "; // 输出 10 20 30
    return 0;
}

还有一个容易被忽略的细节:如果想在for_each中通过仿函数修改元素,参数必须传引用(如void operator()(int& x)),传值只会修改副本。而transform原地修改版本中,lambda的参数按值传递即可,因为修改是通过返回值写回完成的。理解了这一点,两者在修改语义上的区别就非常清晰了。

总结一下选型经验:只需要遍历执行副作用(打印、统计、通知)时用for_each或range-based for;需要把元素经过某种运算变成新值时用transform;需要两个序列按位置做运算时,transform的双区间版本几乎是唯一简洁的选择。另外,如果项目中可以使用C++20,不妨了解一下std::ranges::transformstd::ranges::for_each,它们支持传入整个容器而不用手写begin和end,代码更简洁安全。

C++ STLfor_eachtransform算法修改时间:2026-09-12 00:38:36

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260912/55000.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。