能否让算法泛型处理对象成员以计算复合类型的极值?
为复合类型计算成员独立极值的模板实现
问题背景
需要为容器计算极值(最小值和最大值),但容器可能包含未显式定义operator<的复合类型,因此需要将其成员视为PoD类型分别进行比较。
现有PoD类型极值模板
最初实现的模板对float等基础PoD类型工作正常:
#include <limits> template <typename T> struct extents { // 初始化极值为边界值,便于后续更新 T min = std::numeric_limits<T>::max(), max = std::numeric_limits<T>::lowest(); void update(T value) { min = std::min(min, value); max = std::max(max, value); } T midpoint() const { return std::midpoint(min, max); } };
使用示例(PoD类型)
float a[] = { 1.0f, 5.0f, 2.0f, 3.0f }; extents<float> ext; for (auto v : a) { ext.update(v); }
复合类型的问题
当处理如vec2这类复合类型时,由于未定义operator<,直接使用上述模板会编译失败。且我们的需求是分别比较每个成员的对应值,而非对复合类型进行整体比较,因此不适合定义通用的operator<。
期望实现的调用方式类似std::ranges::sort,通过指定成员指针来让模板自动处理每个成员的极值:
vec2 va[] = { {1.0f, 3.0f}, {5.0f, 2.0f}, {1.0f, 3.0f} }; extents<vec2> vext(&vec2::x, &vec2::y); for (auto v : va) { vext.update(v); }
解决方案:可变参数模板实现
通过可变参数模板扩展extents,使其支持存储多个成员的独立极值,并在update时自动遍历更新每个成员:
#include <limits> #include <tuple> #include <utility> // 针对单个PoD类型的特化 template <typename T> struct extents<T> { T min = std::numeric_limits<T>::max(); T max = std::numeric_limits<T>::lowest(); void update(T value) { min = std::min(min, value); max = std::max(max, value); } T midpoint() const { return std::midpoint(min, max); } }; // 支持复合类型多成员的模板 template <typename T, typename... Members> struct extents { private: std::tuple<extents<std::decay_t<decltype(std::declval<T>().*std::declval<Members>())>>...> member_extents; std::tuple<Members...> members; template <std::size_t... Is> void update_impl(const T& value, std::index_sequence<Is...>) { (std::get<Is>(member_extents).update(value.*std::get<Is>(members)), ...); } public: explicit extents(Members... ms) : members(std::make_tuple(ms...)) {} void update(const T& value) { update_impl(value, std::index_sequence_for<Members...>{}); } template <std::size_t I> auto& get() { return std::get<I>(member_extents); } template <std::size_t I> const auto& get() const { return std::get<I>(member_extents); } };
使用示例(复合类型)
// 定义复合类型 struct vec2 { float x; float y; }; int main() { vec2 va[] = { {1.0f, 3.0f}, {5.0f, 2.0f}, {1.0f, 3.0f} }; extents<vec2, decltype(&vec2::x), decltype(&vec2::y)> vext(&vec2::x, &vec2::y); for (auto v : va) { vext.update(v); } // 获取x成员的极值 float x_min = vext.get<0>().min; // 1.0f float x_max = vext.get<0>().max; // 5.0f // 获取y成员的极值 float y_min = vext.get<1>().min; // 2.0f float y_max = vext.get<1>().max; // 3.0f }
方案优势
- 支持任意数量的成员,适配不同结构的复合类型
- 每个成员独立维护极值,完全符合“分别比较对应成员”的需求
- 保留原PoD类型的使用方式,兼容性强
- 调用逻辑与
std::ranges::sort一致,学习成本低
内容的提问来源于stack exchange,提问作者Damir Tenishev
相关产品推荐
相关产品推荐

