如何使用STL算法单次遍历获取向量各分量的min/max值
单次遍历的STL实现方案
你需要的单次遍历统计三个分量独立极值的需求,可以直接用std::transform_reduce实现,不需要两次遍历数据集。
核心逻辑是把归约的状态设置为std::pair<vec3, vec3>类型,第一个元素存当前遍历到的各分量最小值,第二个存各分量最大值,遍历过程中每遇到一个点就同步更新两个极值,全程只遍历一次容器,时间复杂度和手写原生for循环完全一致。
参考实现如下:
#include <algorithm> #include <vector> #include <utility> #include <stdexcept> struct vec3 { float x, y, z; }; std::pair<vec3, vec3> minmax_elements(const std::vector<vec3>& points) { if (points.empty()) { // 空容器场景按业务需求处理,此处示例为抛出异常 throw std::invalid_argument("points container cannot be empty"); } return std::transform_reduce( points.cbegin() + 1, points.cend(), std::make_pair(points.front(), points.front()), // 归并两个局部极值结果,得到合并后的全局极值 [](const std::pair<vec3, vec3>& lhs, const std::pair<vec3, vec3>& rhs) { return std::pair{ vec3{ std::min(lhs.first.x, rhs.first.x), std::min(lhs.first.y, rhs.first.y), std::min(lhs.first.z, rhs.first.z) }, vec3{ std::max(lhs.second.x, rhs.second.x), std::max(lhs.second.y, rhs.second.y), std::max(lhs.second.z, rhs.second.z) } }; }, // 将单个点转换为极值对(单个点的min和max都是自身) [](const vec3& p) { return std::make_pair(p, p); } ); }
注:
std::minmax_element不适用该场景,该算法返回的是整个元素集合中的最小、最大元素迭代器,判断逻辑基于整个元素的比较规则;但分量独立统计时,x、y、z三个维度的极值大概率不属于同一个vec3元素,因此无法直接用该算法实现需求。
模板参数推导失败的原因
你之前调用std::reduce时编译器无法自动推导BinaryOp类型,核心原因是重载歧义:
你自定义的min、max函数和标准库的std::min、std::max同名,当你直接把函数名min/max作为实参传入时,编译器在名字查找阶段会找到多个同名重载(你定义的vec3版本、标准库的多个泛型重载版本),无法自动确定你需要取哪个重载的函数地址,自然无法完成模板参数推导。
除了你现在用的显式指定模板参数的方案,更简洁的解决方式是把比较逻辑封装在lambda中传入,lambda是编译器生成的独一无二的类型,不存在重载歧义,编译器可以正常完成类型推导,示例写法:
vec3 vmin = std::reduce( points.cbegin(), points.cend(), points.front(), [](const vec3& a, const vec3& b) { return vec3{ std::min(a.x, b.x), std::min(a.y, b.y), std::min(a.z, b.z) }; } );
另外你原来的reduce实现存在一个边界问题:如果传入的points是空容器,直接取points.front()会触发未定义行为,建议补充空容器判断逻辑。
内容的提问来源于stack exchange,提问作者jozxyqk

