前言
严格弱序,stick weak ordering,在以下场景会涉及到
- 对一个容器进行排序时,如使用
std::sort
- 使用有序关联容器时,如使用
std::set
、std::map
- 使用
std::less
时 - 重载
<
操作符(小于)
其中,std::less
与<
操作符都需要满足严格弱序规则。
作用
对于内置类型,我们有三种方式来判断两个值的大小关系,即<
、>
、==
。而对于自定义类型,实现这三种操作符是很累的。
但是,有了严格弱序的概念,我们实现严格弱序即可,而无需实现这三种操作符。
判定规则
假设bool comp(T lhs, T rhs)
函数满足严格弱序。那么,我们如何比较a
、b
的大小呢?
- 若
comp(a, b) == true
,则判定a<b
- 若
comp(b, a) == true
,则判定a>b
- 若
comp(a,b)==false && comp(b,a)==false
,则判定a==b
原理
事实上,
<
其实就是严格弱序,而<=
就不是严格弱序
<号(满足严格弱序)
bool comp1(int lhs, int rhs)
{
//lhs, 全称Left-hand operand,意为左操作数
//rhs, 全称Right-hand operand,意为右操作数
return lhs < rhs;
}
a | b | comp1(a, b) 即a<b | comp1(b, a) 即b<a | comp1(a,b)==false && comp1(b,a)==false |
---|---|---|---|---|
1 | 2 | 1<2 => true | 2<1 => false | false |
2 | 1 | 2<1 => false | 1<2 => true | false |
1 | 1 | 1<1 => false | 1<1 => false | true |
因此,根据严格弱序的判定规则,comp1
能够成功区分a、b的大小。
<=号(不满足严格弱序)
bool comp2(int lhs, int rhs)
{
return lhs <= rhs;
}
a | b | comp2(a, b) 即a<=b | comp2(b, a) 即b<=a | comp2(a,b)==false && comp2(b,a)==false |
---|---|---|---|---|
1 | 2 | 1<=2 => true | 2<=1 => false | false |
2 | 1 | 2<=1 => false | 1<=2 => true | false |
1 | 1 | 1<=1 => true | 1<=1 => true | false |
因此,根据严格弱序的规则,comp2
不能够区分a==b
的情况。
总结
简而言之,严格弱序其实就是:
- 对于相同的元素,无论是
comp(a, b)
,还是comp(b, a)
都要返回false - 如此,才能判断
a==b
!
示例
编写严格弱序的方式有很多中,这就涉及C++的语法,本文并不展开。
以Point3D
为例,编写其严格弱序的函数。Point3D比较复杂,因为涉及到浮点数判断,我们需要定义一个阈值来判定浮点数相等,本示例中使用的是1e-6。
#include <iostream>
#include <set>
struct Point3D
{
double x, y, z;
Point3D(double xx, double yy, double zz) : x(xx), y(yy), z(zz) {}
bool operator==(const Point3D& rhs) const
{
auto equal_zero = [](double v)->bool
{
return -1e-6 < v && v < 1e-6; //相等阈值1e-6
};
return equal_zero(this->x - rhs.x)
&& equal_zero(this->y - rhs.y)
&& equal_zero(this->z - rhs.z);
}
bool operator<(const Point3D& rhs) const
{
//根据严格弱序的规则:对于a==b的情况,无论comp(a, b),还是comp(b, a),两者都要返回false
if (*this == rhs) return false;
//排序方式,优先级:x轴优先级 > y轴 > z轴
if (x < rhs.x) return true;
else if (x > rhs.x) return false;
else if (y < rhs.y) return true;
else if (y > rhs.y) return false;
else if (z < rhs.z) return true;
else return false;
}
friend std::ostream& operator<<(std::ostream& os, const Point3D& pnt)
{
os << pnt.x << '\t' << pnt.y << '\t' << pnt.z;
return os;
}
};
int main()
{
std::set<Point3D> pointSet;
pointSet.emplace(4.4302740239554605, 3.25, 1.99999999998); //A
pointSet.emplace(4.4302740239554605, 3.25, 2.00000000000); //==A
pointSet.emplace(4.5, 3.25, 1.99999999998); //x > A.x
pointSet.emplace(4.4302740239554605, 4.0, 1.99999999998); //y > A.y
pointSet.emplace(4.4302740239554605, 3.25, 3.0); //z > A>z
std::cout.precision(10);
for (const auto& item : pointSet)
{
std::cout << item << std::endl;
}
return 0;
}
输出
4.430274024 3.25 2
4.430274024 3.25 3
4.430274024 4 2
4.5 3.25 2