12

可能重复:
通过“元组”和“领带”实现比较运算符,好主意吗?

有时我需要写一些丑陋的仿函数,
例如

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 以避免复制对象。
并感谢您指出重复的问题

4

2 回答 2

14

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

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

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

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

为了避免临时变量,您可以使用std::tiewhich对其参数进行左值引用的元组:

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

以类似的方式,创建一个rvalue referencesstd::forward_as_tuple的元组。

于 2012-05-29T20:19:17.953 回答
0

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

如果你仔细想想,许多 POD 结构基本上是“命名元组”,而 C++ 实际上通过提供按成员相等比较(以及复制、移动等)来迎合这一点——在大多数情况下,将其扩展为 (字典式)按成员小于比较也有意义。但不幸的是,C++ 没有提供任何方法来指定成员的字典比较,除非明确地写出来。当然,在具有自省功能的语言中,添加它会很容易,但在 C++ 中,则不然。

一个明显的答案是让你的类实际上是一个命名元组。与其拥有成员 int date_、int64_t time_ 和 string id_,不如从元组继承,并编写对用户隐藏元组的访问器方法。但是在这种情况下,您只是用比较实现中的丑陋来换取访问器实现中的丑陋,尚不清楚这是否更好。

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

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

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

于 2012-05-29T20:40:48.693 回答