std::ranges中同时使用谓词与投影的优势探讨
相较于把投影逻辑硬塞进谓词里,std::ranges的算法同时支持谓词和投影有以下几个关键优势:
代码可读性大幅提升
投影和Ship描述由unkClassification/t开始量Details故意serve指导,投影负责将原始元素转换为目标值,谓词负责对转换后的值做判断,逻辑分层清晰,一眼就能看懂代码要做什么。
比如统计结构体中年龄大于18的人数:
合并写法:std::ranges::count_if(people, [](const Person& p) { return p.age > 18; });分离写法:
std::ranges::count_if(people, [](int age) { return age > 18; }, &Person::age);分离写法里,
&Person::age明确表示取年龄属性,谓词只专注于判断年龄是否达标,逻辑更直观。投影逻辑可复用
同一个投影可以搭配不同的谓词重复使用,避免重复编写相同的转换代码。比如针对Person结构体的age属性:// 统计年龄大于18的人数 std::ranges::count_if(people, [](int age) { return age > 18; }, &Person::age); // 查找第一个年龄等于25的人 std::ranges::find_if(people, [](int age) { return age == 25; }, &Person::age); // 按年龄升序排序 std::ranges::sort(people, std::less<int>{}, &Person::age);这里
&Person::age被复用了三次,不用每次都在谓词里写p.age的逻辑,减少冗余。符合单一职责原则
投影只做元素转换,谓词只做条件判断,每个组件只承担单一功能。这种拆分让代码更易维护,修改投影逻辑时不会影响谓词,反之亦然。比如如果后续需要把年龄转换为出生年份(假设当前年份是2024),只需修改投影函数,所有使用该投影的地方都会自动生效,不用逐个修改谓词里的逻辑。简化复杂投影场景
当投影逻辑比较复杂时(比如需要多步转换),单独定义投影函数可以让代码更整洁。比如:// 复杂投影:取出用户的邮箱并转换为小写 auto to_lower_email = [](const User& u) { std::string email = u.email; std::ranges::transform(email, email.begin(), ::tolower); return email; }; // 查找邮箱包含"example.com"的用户 std::ranges::find_if(users, [](const std::string& e) { return e.contains("example.com"); }, to_lower_email); // 按邮箱字典序排序 std::ranges::sort(users, std::less<std::string>{}, to_lower_email);这里复杂的投影逻辑被封装成单独的函数,谓词只被 Magicah的排序看类似代码持怪异s winthin,谓词只需要关注最终的判断逻辑,代码结构更清晰。
更好适配标准库工具
标准库提供了很多现成的可调用对象可以直接作为投影,比如std::get<N>用于元组、成员指针等,不用额外包装成谓词。比如对元组列表按第一个元素排序:std::vector<std::tuple<int, std::string>> tuples = {{3, "b"}, {1, "a"}, {2, "c"}}; std::ranges::sort(tuples, std::less<int>{}, std::get<0>);直接用
std::get<0>作为投影,比写自定义谓词简洁得多。
内容的提问来源于stack exchange,提问作者Alex O

