使用make_tuple进行比较

using make_tuple for comparison

本文关键字:比较 tuple make 使用      更新时间:2023-10-16

可能的重复项:
通过"元组"和"领带"实现比较运算符,一个好主意?

有时我需要写一些丑陋的函子
例如

lhs.date_ < rhs.date_ ||
lhs.date_ == rhs.date_ && lhs.time_ < rhs.time_ ||
lhs.date_ == rhs.date_ && lhs.time_ == rhs.time_ && lhs.id_ < rhs.id_ .....

这真的让我很恼火。
所以我开始避免这样写:

std::make_tuple( lhs.date_, lhs.time_, lhs.id_ ) < 
    std::make_tuple(rhs.date_, rhs.time_, rhs.id_ );

几乎很高兴,但请注意,我可能不是出于元组的目的使用元组,这让我担心。

你能批评这个解决方案吗?
还是一个好习惯?
你如何避免这样的比较?

更新:
感谢您指向 std::tie 以避免复制对象。
并感谢您指出重复的问题

std::tuple比较运算符的声明指出:

按字典顺序比较 lhs 和 rhs,即比较第一个元素(如果它们是等效的),则比较第二个元素,如果它们是等效的,则比较第三个元素,依此类推。

因此,您正在做的事情,除了有可能创建不需要的临时(可能经过优化)之外,对我来说似乎还可以。

请注意,等价意味着!( lhs < rhs ) && !( rhs < lhs )而不是lhs == rhs,这就是相等。假设等相等对你的类意味着相同,这没关系。请注意,这与例如通过密钥访问set/map没有什么不同。

为了避免临时性,您可以使用std::tie,它对其参数进行左值引用元组:

std::tie( lhs.date_, lhs.time_, lhs.id_ ) < 
    std::tie( rhs.date_, rhs.time_, rhs.id_ );

以类似的方式,std::forward_as_tuple 创建右值引用的元组。

您可能最好使用 tie 而不是 make_tuple(取决于是否值得避免复制/移动成员),但除此之外,这将起作用,并且没有什么可怕的。

如果你考虑一下,许多 POD 结构基本上是"命名元组",C++实际上通过提供按成员相等比较(以及复制、移动等)来满足这一点。——在大多数情况下,将其扩展到(词典编纂)按成员小于比较也是有意义的。但不幸的是,C++没有提供任何方法来指定成员的字典顺序比较,除非明确写出来。当然,在具有内省功能的语言中,添加起来很容易,但在C++中,它不是。

一个显而易见的答案是实际上使您的类实际上是一个命名元组。而不是让成员 int date_、int64_t time_ 和字符串id_,而是从元组继承,并编写对用户隐藏元组的访问器方法。但是在这种情况下,您只是将比较实现中的丑陋换成了访问器实现中的丑陋,并且不清楚这是否更好。

你可以使用宏来包装丑陋,但这可能不是更好:

DEFINE_NAMED_TUPLE_CLASS(MyDate, int date, int64_t time, string id)
  ...
END_NAMED_TUPLE_CLASS

或者,您可以构建自己的外部代码生成器,该生成器允许您预处理具有"named_tuple_class"的扩展C++,它只需将标记更改为"class",然后定义比较运算符即可处理该。但这是很多工作。