[C++STL] 严格弱序(less函数、小于号重载)

文章介绍了C++中严格弱序的概念及其在排序、关联容器和比较函数中的应用。严格弱序要求比较函数能区分所有元素,包括相等的情况。文章通过示例说明了如何为自定义类型(如Point3D)实现严格弱序,并指出内置类型的比较操作符<实际上满足严格弱序,而<=不满足。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

严格弱序,stick weak ordering,在以下场景会涉及到

  1. 对一个容器进行排序时,如使用std::sort
  2. 使用有序关联容器时,如使用std::setstd::map
  3. 使用std::less
  4. 重载<操作符(小于)

其中,std::less<操作符都需要满足严格弱序规则。

作用

对于内置类型,我们有三种方式来判断两个值的大小关系,即<>==。而对于自定义类型,实现这三种操作符是很累的。

但是,有了严格弱序的概念,我们实现严格弱序即可,而无需实现这三种操作符。

判定规则

假设bool comp(T lhs, T rhs)函数满足严格弱序。那么,我们如何比较ab的大小呢?

  1. comp(a, b) == true,则判定a<b
  2. comp(b, a) == true,则判定a>b
  3. 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;
}
abcomp1(a, b)
即a<b
comp1(b, a)
即b<a
comp1(a,b)==false && comp1(b,a)==false
121<2 => true2<1 => falsefalse
212<1 => false1<2 => truefalse
111<1 => false1<1 => falsetrue

因此,根据严格弱序的判定规则,comp1能够成功区分a、b的大小。

<=号(不满足严格弱序)

bool comp2(int lhs, int rhs)
{
	return lhs <= rhs;
}
abcomp2(a, b)
即a<=b
comp2(b, a)
即b<=a
comp2(a,b)==false && comp2(b,a)==false
121<=2 => true2<=1 => falsefalse
212<=1 => false1<=2 => truefalse
111<=1 => true1<=1 => truefalse

因此,根据严格弱序的规则,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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

geodoer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值