资讯动态

深入理解C++ 容器 std::valarray

发布时间:2026/10/23 9:14:46 来源:尧图企业网站定制
std::valarray是 C 标准库中一个为数值计算而设计的数组类模板。它最初的设计目标非常明确通过减少指针别名aliasing带来的限制让编译器能够对批量数值运算进行高效的向量化优化。然而由于历史原因和后续标准发展的滞后它在现代 C 中的实际使用并不广泛地位也有些尴尬。下面的介绍会详细解释它的特点、用法并与更通用的std::vector进行对比。 核心设计理念std::valarray的核心思想是将整个数组视为一个数学向量。因此它原生支持许多在std::vector上需要手动循环或调用算法才能完成的逐元素操作。元素级数学运算你可以直接对整个valarray进行、-、*、/、%等运算以及sin、cos、exp、log等数学函数。例如v3 sin(v2 v1 * 3)这样的表达式在valarray上是直接成立的。切片Slicing它内置了强大的子集访问机制可以像处理多维数组一样方便地提取和修改一维数组中的特定元素这是std::vector不具备的能力。为性能而设计标准库实现被鼓励采用表达式模板Expression Templates等技术在计算a b c d时可能无需为b c生成临时数组从而减少内存分配和拷贝。️ 主要接口与功能std::valarray的接口围绕其数值计算定位展开。构造与初始化可以像普通数组一样用初始值列表构造也可以用一个值填满整个数组或者从 C 风格数组拷贝数据。cpp#include valarray std::valarrayint v1 {1, 2, 3, 4, 5}; // 从 initializer_list 构造 std::valarraydouble v2(10, 3.14); // 10 个元素全部初始化为 3.14 int arr[] {5, 6, 7}; std::valarrayint v3(arr, 3); // 从 C 数组构造数学运算与函数这是valarray的亮点。所有算术运算符、-、*、/、%、、|、^、、、~、!和复合赋值运算符、-等都按元素逐一进行。cppstd::valarraydouble a {1.0, 2.0, 3.0}; std::valarraydouble b {4.0, 5.0, 6.0}; auto c a b; // c {5.0, 7.0, 9.0} a * 2.0; // a {2.0, 4.0, 6.0} auto d std::sin(a); // 对每个元素应用 sin()成员函数sum()、min()、max()求和、最小值、最大值。shift(int n)、cshift(int n)逻辑移位空位补零/补零和循环移位。apply(T func(T))对每个元素应用一个函数。resize(size_t n)调整大小但会丢弃原有数据并可能触发重新分配。四种切片Subset操作valarray通过四种辅助类来定义子集支持对数组的特定部分进行读、写或运算。切片类型辅助类/构造作用描述示例v为一个valarraySlicestd::slice(start, size, stride)按步长选取等距元素。v[std::slice(0, 3, 2)]选取索引0, 2, 4的元素。GSlicestd::gslice(start, lengths, strides)通用切片可模拟多维数组的切片是slice的推广。从一维数组中按多维布局提取“列”或“行”。Maskstd::valarraybool用一个布尔掩码数组选取对应位置为true的元素。v[mask]其中mask {true, false, true, ...}。Indirectstd::valarraysize_t用一个索引数组选取任意位置的元素。v[idx]其中idx {4, 1, 3, ...}。所有切片返回的都是轻量的代理对象如slice_array、mask_array并非新的valarray。它们支持赋值可以直接修改原数组的对应元素。 与std::vector的关键对比选择valarray还是vector取决于你的任务性质。特性std::valarraystd::vector主要用途数值计算作为数学向量通用动态数组作为容器逐元素运算✅原生支持,-,*,sin()等❌ 需要循环或std::transform切片能力✅强大slice,mask,indirect等❌ 没有内置的视图机制C20std::span也非直接切片动态调整有resize()但会丢弃数据不是主要设计目的✅核心功能支持push_back、resize并保留数据迭代器C11 起有begin()/end()但支持较弱✅完整完全兼容 STL 算法内存分配策略通常按需精确分配无额外容量预留通常会预留额外容量如翻倍以减少分配次数与 STL 兼容性较低被视为“几乎是容器”near-container核心容器与所有 STL 算法和设施无缝集成简单总结如果你需要进行大量整体性的数学向量运算并且会用到切片这类特殊访问模式valarray的语法糖会很有帮助。但对于绝大多数通用编程任务需要动态增删、与 STL 算法配合时std::vector是更稳妥、更通用的选择。⚠️ 版本演进与现状std::valarray在 C11 中得到了移动语义等更新并未被废弃。后续标准有一些微小的修补如 C20 对类型推导的修正C26 计划中的begin/end一致性调整但其核心功能和地位在过去二十多年里基本没有变化。一个关键的现实是主流标准库实现如 libstdc、libc通常并未利用表达式模板等高级技术来充分优化valarray的性能。因此它理论上的性能优势在实际中可能并不明显。在现代 C 中进行严肃的数值计算人们通常会转向Eigen这类专门的线性代数库或者在需要 GPU 加速时选择CUDA/SYCL/std::par等方案。#include iostream #include valarray #include cmath // 辅助函数方便打印 valarray template typename T void print(const std::string name, const std::valarrayT v) { std::cout name [ ; for (const auto x : v) std::cout x ; std::cout ]\n; } int main() { // 1. 基本构造与逐元素运算 std::valarraydouble a {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; std::valarraydouble b {10.0, 20.0, 30.0, 40.0, 50.0, 60.0}; print(a, a); print(b, b); // 逐元素相加、相乘并应用数学函数无需循环 auto c a b; print(a b, c); auto d std::sin(a) * 2.0; // 对 a 的每个元素取 sin再乘以 2 print(sin(a) * 2, d); // 2. 使用 slice 切片 // 选取索引 0, 2, 4 的元素从 0 开始步长 2共 3 个 std::slice s(0, 3, 2); auto even_idx_elements a[s]; // 返回一个 slice_array 代理 print(a[slice(0,3,2)], even_idx_elements); // 直接通过切片赋值修改原数组 a[s] std::valarraydouble{100.0, 200.0, 300.0}; print(a after slice assignment, a); // 3. 使用 mask 切片 std::valarraybool mask {true, false, true, false, true, false}; auto masked_elements b[mask]; print(b[mask], masked_elements); // 4. 使用 indirect 切片 std::valarraysize_t indices {5, 0, 3}; // 选取索引 5, 0, 3 auto indirect_elements b[indices]; print(b[{5,0,3}], indirect_elements); return 0; }

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑