std::transform 是一个泛型算法,用于将源范围元素经函数处理后写入目标范围,不创建新容器且要求目标空间预先分配足够容量。

std::transform 是什么?它不是 for 循环的语法糖
它是一个泛型算法,作用是把一个范围内的每个元素,用给定函数处理后,写入另一个(或同一个)范围。关键点在于:它不创建新容器,只做“就地映射”或“复制映射”,且要求目标范围已有足够空间。
常见错误现象:std::transform 写入空 std::vector 却没预留容量,结果行为未定义(比如静默失败、崩溃或垃圾值);或者误以为它会自动扩容——它不会。
- 必须确保目标迭代器所指范围可写,长度 ≥ 源范围长度
- 支持“原地转换”(输入输出迭代器重叠),但需满足严格弱序要求(如
vec.begin()到vec.end()作输入,vec.begin()作输出是安全的;但vec.begin()+1作输出可能出问题) - 函数对象可以是 Lambda、函数指针或仿函数,参数类型必须匹配源迭代器解引用类型
怎么用 std::transform 把 vector 全部平方?
最典型场景:对 std::vector 做逐元素变换,结果存回原容器或新容器。
正确做法是先保证目标空间存在。写回原容器最简单:
立即学习“C++免费学习笔记(深入)”;
std::vector<int> v = {1, 2, 3, 4}; std::transform(v.begin(), v.end(), v.begin(), [](int x) { return x * x; });
如果想存到新容器,得提前分配好空间(不能直接 push_back):
std::vector<int> src = {1, 2, 3}; std::vector<int> dst(src.size()); // 必须! std::transform(src.begin(), src.end(), dst.begin(), [](int x) { return x + 10; });
- 用
std::back_inserter(dst)看似方便,但std::transform不接受插入迭代器——编译报错:no matching function for call to 'transform' - 若真要动态增长,该用
std::for_each或 range-based for +push_back - lambda 捕获列表为空时,可退化为函数指针,不影响性能
std::transform 和 std::copy_if / std::replace 有什么本质区别?
它们都是单趟遍历算法,但语义和约束完全不同。
std::transform 强制“一对一映射”:输入 N 个元素,必须输出 N 个结果,不允许跳过或增删。
-
std::copy_if是“过滤”,输出数量 ≤ 输入数量,靠谓词决定是否复制 -
std::replace是“就地替换”,不改变容器大小,也不生成新值(只是把旧值替换成指定常量) - 混淆三者会导致逻辑错误:比如想过滤偶数却用了
transform,结果奇数位置被赋成未定义值 - 性能上,
transform通常比手写循环略快(更好向量化),但前提是函数对象无副作用且 trivial
为什么 transform 有时比 range-for 更难调试?
因为迭代器失效、越界、目标空间不足等问题,在 std::transform 中往往不抛异常,而是触发未定义行为,表现随机——比如某次运行正常,换编译器或优化等级就崩。
- 调试时优先检查:输入迭代器范围是否有效、输出迭代器起始位置是否可写、两者距离是否匹配
- 别依赖
auto推导迭代器类型来“省事”,尤其混用const_iterator和iterator时容易编译失败 - 在 debug 模式下,某些标准库实现(如 libstdc++ 的 _GLIBCXX_DEBUG)会检测输出范围不足并 abort,但 release 下静默失败
真正麻烦的从来不是写法,而是它太安静——出问题时既不报错也不提示哪一行越界,只留下一个 core dump 在角落里等你翻半天。