C++中为何需为二元谓词函数定义两个模板参数?
嘿,这个问题我之前也踩过坑,本质是C++谓词模板的类型推导特性和职责分离需求导致的,我给你一步步拆解清楚:
为什么必须给两个二元谓词各设一个模板参数?
1. 两个谓词的核心职责完全不同
你的函数要完成两件独立的事:
- 第一件:判断当前元素是否比已记录的极值更“符合要求”(比如找最大值时,判断当前元素是否大于极值)——这需要比较谓词(比如
std::greater<T>) - 第二件:判断当前元素是否和极值完全相等——这需要相等谓词(比如
std::equal_to<T>)
这俩逻辑根本不绑定:你可能用自定义规则比较(比如按结构体的score成员排序),但用另一个规则判断相等(比如结构体的id成员相同就算相等)。如果共用一个模板参数,就把这俩逻辑硬绑死了,完全丧失了灵活性。
2. 谓词模板的实例化容易出问题
你直接写*first > *lim.front()时,编译器会自动推导操作数类型,调用内置运算符,没啥问题。但换成std::greater<>这类模板时,坑就来了:
- 如果你没指定模板参数(比如用
std::greater{}),C++17的CTAD(类模板实参推导)虽然能自动推导,但如果迭代器指向的是复杂类型(比如const int&、自定义结构体),推导很可能出错。 - 更关键的是:比较谓词和相等谓词的实例化类型可能完全不同。比如你用
std::greater<const MyStruct&>做比较,但相等谓词可能需要std::equal_to<MyStruct>,共用一个模板参数的话,必然导致其中一个谓词的类型不匹配,要么编译报错,要么运行行为异常。
3. 参考Boost的设计思路
你提到参考了boost::minmax_element.hpp,其实Boost的这类算法都会把比较谓词作为独立模板参数,而相等判断要么默认用std::equal_to,要么基于比较谓词推导(比如comp(a,b)和comp(b,a)都为false时认为a等于b)。但如果用户需要自定义相等逻辑,Boost也会提供额外的模板参数——这也侧面说明,分开两个谓词是满足灵活需求的必要设计。
给你的修复建议
如果想让函数完美支持自定义谓词,应该把模板定义成这样:
#include <vector> #include <functional> #include <iterator> template <typename ForwardIt, typename Compare = std::less<>, typename Equal = std::equal_to<>> std::vector<ForwardIt> find_all_extremes(ForwardIt first, ForwardIt last, Compare comp = Compare{}, Equal eq = Equal{}) { std::vector<ForwardIt> result; if (first == last) return result; // 初始化第一个极值 result.push_back(first); ++first; for (; first != last; ++first) { // 用比较谓词判断是否是更优的极值 if (comp(*result.front(), *first)) { result.clear(); result.push_back(first); } // 用相等谓词判断是否等于当前极值 else if (eq(*result.front(), *first)) { result.push_back(first); } } return result; }
这里用了两个独立的模板参数Compare和Equal,各自负责比较和相等判断,还都加了默认值,用户可以按需自定义。
举几个调用的例子:
#include <vector> #include <iostream> int main() { std::vector<int> vec = {3, 1, 4, 1, 5, 9, 9, 2}; // 找所有最大值(默认用std::less,comp(a,b)为true时a < b,所以*result.front() < *first意味着*first更大) auto max_pos = find_all_extremes(vec.begin(), vec.end()); std::cout << "Max positions: "; for (auto it : max_pos) std::cout << std::distance(vec.begin(), it) << " "; // 输出5 6 // 用std::greater找所有最小值 auto min_pos = find_all_extremes(vec.begin(), vec.end(), std::greater<int>{}); std::cout << "\nMin positions: "; for (auto it : min_pos) std::cout << std::distance(vec.begin(), it) << " "; // 输出1 3 // 自定义相等谓词(比如忽略正负) auto custom_eq = [](int a, int b) { return std::abs(a) == std::abs(b); }; auto same_abs_max = find_all_extremes(vec.begin(), vec.end(), std::less<int>{}, custom_eq); std::cout << "\nSame abs max positions: "; for (auto it : same_abs_max) std::cout << std::distance(vec.begin(), it) << " "; // 输出5 6 return 0; }
这样调整后,不管用内置运算符还是标准库谓词模板,都能正常工作了。
内容的提问来源于stack exchange,提问作者Moises Rojo
相关产品推荐
相关产品推荐

