std::transform是C++标准库中对容器元素进行一对一映射变换的算法,适用于原地或跨容器的单/双输入元素转换,如取绝对值、字符转大写、向量加法等;需确保目标空间已分配且迭代器合法。

transform 是什么,什么时候该用它
std::transform 是 C++ 标准库中用于对容器(或任意迭代器范围)内元素做“一对一映射变换”的算法。它不改变容器大小,只把每个输入元素按给定函数处理后,写入目标位置。
适合场景包括:
- 把
vector全部取绝对值、平方、转成字符串 - 把
string每个字符转大写(std::toupper) - 两个容器对应位置相加,存到第三个容器里(双区间版本)
不是所有“批量操作”都该用它——比如要过滤或聚合(求和/最大值),应选 std::remove_if 或 std::accumulate。
单区间 transform:输入一个范围,输出到另一处
最常用形式,原型是:
立即学习“C++免费学习笔记(深入)”;
std::transform(first, last, d_first, unary_op);
注意三点:
-
d_first必须有足够空间容纳结果(不会自动扩容) - 若目标与源重叠(如原地变换),
d_first必须等于first或在其之前,否则行为未定义 -
unary_op必须是可调用对象,接受一个参数,返回变换后值
示例:原地将 vector 所有元素除以 100
std::vectordata = {150.0, 230.0, 98.0}; std::transform(data.begin(), data.end(), data.begin(), [](double x) { return x / 100.0; });
常见错误:
- 忘记提前分配目标空间,比如对空
vector调用transform写入,导致越界写入(没报错但内存损坏) - 传入
std::back_inserter(dest)——这不能用,因为transform需要随机访问迭代器来定位写入位置,而back_inserter是输出迭代器,不支持偏移
双区间 transform:两组输入,一个输出(逐元素二元运算)
原型是:
std::transform(first1, last1, first2, d_first, binary_op);
要求:[first2, first2 + (last1 - first1)) 范围必须合法且可读。
典型用途:
- 向量加法:
v3[i] = v1[i] + v2[i] - 混合缩放:
result[i] = a[i] * scale + b[i]
示例:两个 vector 对应位置相乘,存入 result
std::vectora = {1, 2, 3}, b = {4, 5, 6}; std::vector result(3); std::transform(a.begin(), a.end(), b.begin(), result.begin(), std::multiplies {});
注意:
- 不检查
b是否足够长,越界读取是未定义行为 -
std::multiplies是函数对象,不是{} std::multiplies类型名(后者不带是不完整类型) - C++20 起支持
std::ranges::transform,可自动推导范围长度并更安全,但老项目仍以传统版本为主
容易被忽略的细节:迭代器类型、const 和 move 语义
-
transform 对 const 容器的 begin() 返回 const_iterator,若想写入,目标容器不能是 const,也不能用 cbegin() 作输入(除非你只读不写,但那就没法用了)
- 若元素支持移动(如
std::string),且你想“转移而非拷贝”,不能直接传 lambda 捕获 std::move(x) ——因为 transform 内部仍按值传递参数;正确做法是让 unary_op 接受右值引用或使用 std::move_iterator 包装输入迭代器(较罕见,多数情况拷贝开销可接受)
- 使用
std::transform 处理 std::array 时,记得用 .begin()/.end(),别误用数组名(退化为指针)
transform 对 const 容器的 begin() 返回 const_iterator,若想写入,目标容器不能是 const,也不能用 cbegin() 作输入(除非你只读不写,但那就没法用了) std::string),且你想“转移而非拷贝”,不能直接传 lambda 捕获 std::move(x) ——因为 transform 内部仍按值传递参数;正确做法是让 unary_op 接受右值引用或使用 std::move_iterator 包装输入迭代器(较罕见,多数情况拷贝开销可接受) std::transform 处理 std::array 时,记得用 .begin()/.end(),别误用数组名(退化为指针) 真正麻烦的点往往不在语法,而在边界对齐和内存布局——尤其是当目标容器是另一个容器的子范围(如 subvec)或用 reserve() 但没 resize() 时,transform 会静默写入未初始化内存。










