如何简化通过类Getter方法创建std::tuple的调用方式?
简化C++20多维度排序的Getter调用实现方案
针对你的需求,我们可以利用C++20的Concepts、编译期逻辑和宏简化,实现无需手动传入完整成员函数指针的调用方式,同时保证类型安全。
核心实现思路
通过编译期自动拼接deliver_前缀,结合Concepts校验类型是否拥有对应Getter方法,最终简化调用语法。以下是可直接落地的代码方案:
完整可编译代码
#include <tuple> #include <vector> #include <algorithm> #include <concepts> #include <type_traits> // 基础Getter包装器,用于统一类型标识 struct GetterBase {}; // 模板:绑定具体成员函数的Getter包装器 template<typename ObjType, auto (ObjType::*GetterFunc)() const> struct BoundGetter : GetterBase { static auto extract(const ObjType& obj) { return (obj.*GetterFunc)(); } }; // 核心宏:自动匹配Getter(优先找deliver_前缀,不存在则直接匹配传入名称) #define AUTO_GET(NAME) \ std::conditional_t< \ requires(const Obj& obj) { obj.deliver_##NAME(); }, \ BoundGetter<Obj, &Obj::deliver_##NAME>, \ BoundGetter<Obj, &Obj::NAME> \ > // 多维度排序函数:支持任意数量的Getter参数 template<typename Obj, typename... Getters> requires (std::derived_from<Getters, GetterBase> && ...) void sort_by_getters(std::vector<Obj>& obj_list) { std::ranges::sort(obj_list, [](const Obj& a, const Obj& b) { return std::make_tuple(Getters::extract(a)...) < std::make_tuple(Getters::extract(b)...); }); } // ------------------------------ // 示例使用 // ------------------------------ struct Agg { int deliver_j() const { return j_val; } std::string deliver_k() const { return k_str; } // 假设存在一个直接命名的Getter(用于测试直接匹配逻辑) double priority() const { return prio; } int j_val = 0; std::string k_str; double prio = 0.0; }; int main() { std::vector<Agg> data; // 调用示例:直接传入j、deliver_k、priority,宏自动匹配对应Getter sort_by_getters<Agg, AUTO_GET(j), AUTO_GET(deliver_k), AUTO_GET(priority)>(data); return 0; }
关键特性解析
- 类型安全校验:通过
requires子句在编译期检查目标类型是否拥有对应Getter方法,不存在则直接编译报错,避免运行期错误。 - 自动前缀拼接:
AUTO_GET宏会优先检查deliver_XXX形式的方法,不存在则尝试直接匹配XXX方法,完全符合你的需求。 - 适配C++20标准:使用
std::ranges::sort替代传统std::sort,结合Concepts做编译期约束,符合现代C++风格。 - 扩展性强:新增Getter无需修改排序函数,只需在调用时传入对应的名称即可。
无宏替代方案(适合Getter数量较少的场景)
如果不想依赖宏,可以手动定义Getter标签并特化包装器,代码更直观但需要额外的定义工作:
// 定义Getter标签 struct JTag {}; struct DeliverKTag {}; struct PriorityTag {}; // 特化包装器,绑定到具体方法 template<> struct BoundGetter<Agg, JTag> { static auto extract(const Agg& obj) { return obj.deliver_j(); } }; template<> struct BoundGetter<Agg, DeliverKTag> { static auto extract(const Agg& obj) { return obj.deliver_k(); } }; template<> struct BoundGetter<Agg, PriorityTag> { static auto extract(const Agg& obj) { return obj.priority(); } }; // 调用方式 sort_by_getters<Agg, BoundGetter<Agg, JTag>, BoundGetter<Agg, DeliverKTag>, BoundGetter<Agg, PriorityTag>>(data);
这种方式完全避免宏,但需要为每个Getter手动编写标签和特化代码,适合Getter数量固定且较少的遗留系统。
内容的提问来源于stack exchange,提问作者Thomas G.
相关产品推荐
相关产品推荐

