结论:小数组用fill或构造初始化即可;大数组(>10k)填固定值时fill可能被优化为memset,快于手写循环;仅需递增整数序列时用iota,但它不适用浮点或索引模拟场景。

直接说结论:小数组用 fill 或构造初始化就行,别折腾;大数组(>10k 元素)且值固定时 fill 可能被编译器自动优化成 memset,比手写循环快;要填递增整数序列才轮到 iota,但它不适用于浮点或索引模拟场景。
std::fill 填什么、怎么填、为什么不能乱传参数
fill 的作用就是把一段连续内存全设成同一个值,它不关心类型语义,只做赋值操作。常见误用是把它当“重置函数”用在未分配空间的容器上:
-
std::vector<int> v;</int>后直接std::fill(v.begin(), v.end(), 42)—— 这段代码不报错但什么也不会发生,因为v是空的,begin() == end() - 对 C 风格数组写
fill(arr, 10, 0)—— 编译失败,fill要三个迭代器参数,正确写法是std::fill(arr, arr + 10, 0)或更安全的std::fill(std::begin(arr), std::end(arr), 0) - 对
std::vector<bool></bool>使用fill—— 行为未定义,因为vector<bool></bool>的iterator是代理类,不满足泛型算法要求
std::iota 适合填什么、不适合填什么
iota 不是“从 0 开始填索引”,它是“从你给的起始值开始,每次加 1 写进去”。所以 std::iota(v.begin(), v.end(), 100) 填出来是 100, 101, 102...,不是 0, 1, 2...。它真正适用的场景非常窄:
- 需要严格等差整数序列,步长固定为 1,且元素类型支持
operator++ - 目标容器已预分配空间(
resize或构造时指定大小),iota不扩容 - 不用在
std::forward_list上——它只接受前向迭代器及以上,forward_list::iterator不支持自增后赋值的稳定语义 - 别试图用它填
double序列(如 0.0, 0.1, 0.2),浮点累加误差会快速放大;该用std::generate配 lambda
性能差异在哪?编译器说了算
单看函数本身,fill 和 iota 都是线性遍历,O(n) 时间复杂度。真实性能差距取决于编译器能否识别模式并替换为底层指令:
- 对全零填充(
fill(..., 0)),GCC/Clang 在-O2下大概率转成memset,尤其是对齐的std::vector<char></char>或int数组 - 对非零常量填充(如
fill(..., 42)),现代编译器也能向量化(AVX2 展开),但前提是数组长度足够、内存对齐、无别名干扰 -
iota很难被优化成 SIMD 指令,因为每个元素依赖前一个值;它的优势在语义清晰和避免手写循环出错,不在速度 - 实测中,100 万
int的fill和iota耗时差不到 5%,但手写带边界检查的for循环可能慢 20%+(尤其未开优化时)
容易被忽略的关键点
最常踩的坑不是选错函数,而是忽略内存模型和容器状态:
-
std::array和std::vector行为一致,但std::vector<bool></bool>和std::deque不支持fill/iota标准用法 - 用
fill_n时必须自己确保first + n不越界,它不查容量也不抛异常;std::vector::assign(n, val)更安全,但会改变size() - 初始化裸内存(比如
new int[1000])优先用std::fill,别用memset填非零非 -1 值——类型安全比速度重要
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











