一、入门
1、什么是比较器
在 C++ 中,比较器是一个可调用对象(函数、函数对象或 Lambda 表达式),用于定义元素之间的比较规则。
用途:通常作为参数传递给标准库中的排序函数或关联容器,以指定元素的顺序。
cpp
std::vector<int> numbers = {5, 2, 8, 1, 9};
// 使用 Lambda 表达式作为比较器进行降序排序
std::sort(numbers.begin(), numbers.end(), [](int a, int b) {
return a > b;
});
Lambda 表达式 [](int a, int b) { return a > b; }
作为比较器传递给 std::sort
函数,用于定义降序排序规则。
2、什么是通透比较器(Transparent Comparator)
通透比较器是一种允许模板参数自动推导类型的比较器
解决泛型编程中比较器与容器元素类型严格绑定的问题(例如std::set
默认要求比较器类型与元素类型一致)
通过使用std::less<>
(C++17起默认支持通透性),可以直接比较不同类型的数据(如字符串和字符串视图),无需显式类型转换
cpp
std::set<std::string, std::less<>> s; // 使用通透比较器
s.find("hello"); // 可直接传入const char*类型,无需构造std::string
二、进阶
1、如何使用自定义的函数对象作为比较器来对自定义类型的对象进行排序?
cpp
// 定义自定义比较器函数对象
class AgeComparator {
public:
bool operator()(const Person& p1, const Person& p2) const {
return p1.age < p2.age;
}
};
使用如下:
cpp
class Person {
public:
std::string name;
int age;
Person(const std::string& n, int a) : name(n), age(a) {}
};
int main() {
std::vector<Person> people = {
{"Alice", 25},
{"Bob", 20},
{"Charlie", 30}
};
// 使用自定义比较器进行排序
std::sort(people.begin(), people.end(), AgeComparator());
return 0;
}
2、通透比较器与传统比较函数有何区别?
- 传统比较器 :需显式指定元素类型(如
std::less<std::string>
),要求参数类型严格匹配,否则需要类型转换。 - 通透比较器 :通过类型透传(
operator()(const auto& a, const auto& b)
)允许模板自动推导类型,支持异构比较。例如std::less<>
可以比较std::string
和const char*
3、为什么通透比较器需要严格弱序保证?违反时可能引发哪些问题?
- 严格弱序要求 :传递性(若
a < b
且b < c
,则a < c
)、反对称性(a < b
和b < a
不能同时成立)等。 - 问题示例 :若比较器未满足严格弱序,
std::set
等容器可能出现元素重复或查找失败(未定义行为)
三、高阶
1、如何实现一个自定义通透比较器?核心机制是什么?
类型透传 :通过模板参数推导(如operator()
的参数为通用引用)
严格弱序:确保比较逻辑满足严格弱序规则(传递性、反对称性等)
cpp
struct LengthCompare {
template <typename T, typename U>
bool operator()(T&& a, U&& b) const {
return a.size() < b.size(); // 支持任意有size()成员的类型
}
};
std::set<std::string, LengthCompare> s; // 按长度排序
2、C++20的三向比较运算符(<=>
)与通透比较器的关系是什么?
<=>
(太空船运算符)简化了比较逻辑的定义,通过返回std::strong_ordering
等类型自动生成全部比较运算符(如<
、==
)
通透比较器可与<=>
结合,例如在自定义比较器中直接调用a <=> b
实现类型透明的比较逻辑:
cpp
struct TransparentCompare {
template <typename T, typename U>
auto operator()(T&& a, U&& b) const -> decltype(a <=> b) {
return a <=> b; // 自动推导比较结果类型
}
};
3、通透比较器在模板元编程中的优化意义是什么?举例说明SFINAE如何支持通透性。
优化意义 :避免模板特化冗余,通过单一模板适配多种类型。例如std::map
使用通透比较器时,find()
可接受任意可比较类型,减少运行时构造临时对象的开销。
SFINAE
(Substitution Failure Is Not An Error)替换失败并非错误, 是 C++ 模板元编程的核心技术,允许编译器在模板参数替换失败时忽略该候选函数/类,而非直接报错。其核心思想是:通过编译期的条件筛选,选择最合适的模板实现。
cpp
template <typename T>
struct Compare {
template <typename U>
auto operator()(const T& a, const U& b) const
-> decltype(a < b, std::true_type{}) { // 仅当a < b合法时启用
return a < b;
}
};
4、设计一个支持异构查找的容器,要求使用通透比较器和C++20特性
std::strong_ordering
是 C++20 引入的三向比较运算符(<=>
,飞船运算符)的返回类型之一,用于表示两个对象之间的严格顺序关系。它属于强序(Strong Ordering),要求比较结果满足以下特性:
- 传递性 :若
a < b
且b < c
,则a < c
。- 反对称性 :若
a < b
,则b > a
。- 完全性:任意两个值必须可比较(不存在无法比较的情况)
此外还有:std::weak_ordering、std::partial_ordering
cpp
#include <set>
#include <string>
#include <compare>
struct HeterogeneousCompare {
template <typename T, typename U>
auto operator()(T&& a, U&& b) const -> std::strong_ordering {
return a <=> b; // 使用三向比较运算符
}
};
std::set<std::string, HeterogeneousCompare> s;
s.emplace("apple");
bool exists = s.contains("apple"); // 直接传入字符串字面量