【问题标题】:Why do I have to implement operator< when using operator() for sorting a std::set为什么在使用 operator() 对 std::set 进行排序时必须实现 operator<
【发布时间】:2019-01-10 19:07:39
【问题描述】:

在代码审查期间,我的一位同事正在使用结构对 std::set 进行排序。我对 C++ 还是很陌生,必须自己实现它才能完全理解它。可悲的是,我遇到了一些困难,因为在我实现了结构的 operator() 之后,MSVC 也强迫我实现了 operator

如果我使用结构对 std::set 进行排序,有人可以解释一下为什么必须同时实现这两个运算符吗?我猜想不需要 operator

class Hallo {
int one;
int two;
public:
Hallo(int one, int two);

bool operator < (const Hallo& rhs) const
{
    return one < rhs.GetOne();
}

struct cmpStruct{
bool operator()(Hallo const &lhs, Hallo const &rhs) const
{
        return lhs.GetOne() < rhs.GetOne();
}

int main(int ac, char* av[]){
const Hallo a{ 1, 1 };
const Hallo b{ 2, 2 };
const Hallo c{ 3, 3 };
const Hallo d{ 5, 5 };

std::set<Hallo, Hallo::cmpStruct> sortedList{};
std::set<Hallo> unsortedList{};

sortedList.insert(b);
sortedList.insert(c);
sortedList.insert(a);
sortedList.insert(d);

unsortedList.insert(b);
unsortedList.insert(c);
unsortedList.insert(a);
unsortedList.insert(d);

【问题讨论】:

  • 其实,你有一些选择:1) 为你的stuct 重载operator&lt;; 2) 编写一个独立的函数用于比较structs 并将其传递给std::sortstd::set; 3) 编写一个函数对象,重载operator&lt; 用于比较structs。
  • 为什么std::set&lt;Hallo&gt; unsortedList{};unsortedList所有集都已排序。
  • 不确定这里问的是什么。如果没有为set 提供比较器(如在第二个示例中),则调用默认std::less,这反过来将调用结构本身上的运算符小于(独立或成员)。这就是为什么在第二种情况下您必须自己提供这样的运算符。
  • @ThomasMatthews 你错过了一个 - std::less 的专业化

标签: c++ struct set compare


【解决方案1】:

如果我使用结构来对 std::set 进行排序,有人可以解释一下为什么有必要同时实现这两个运算符吗?

因为您使用class Hallo 作为键创建了两个std::set 实例,并且在第一个实例中您明确使用cmpStruct 作为仿函数,但在第二个实例中您隐含使用std::less,如documentation 中所述/p>

template<
    class Key,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<Key>
> class set;

std::less 使用“一些基本的比较功能”,即operator&lt; documentation

用于执行比较的函数对象。除非专门,在类型 T 上调用运算符。

重点是我的。因此,除非您将 std::less 专门用于 class Hallo,或者不会将 std::set 实例化中的 std::less 替换为其他东西,否则它将需要 operator&lt; 作为方法或独立函数。

【讨论】:

  • 感谢您的帮助,这就是我正在寻找的答案。编译器不允许我使用“>”比较来实现 operator
【解决方案2】:

默认情况下,std::set 使用 operator &lt; 比较两个 Hallo 实例。这就是为什么您需要定义一个operator&lt; 函数来比较它们。比较structs 没有默认操作。

编辑 1:指定排序或比较函数
std::set 构造函数之一允许您指定用于比较 Hallo 实例的函数。如果您不想将重载的 operator&lt; 方法添加到结构中,则需要将比较两个 Hallo 实例的函数传递给 std::set 构造函数。同样,structclass 实例没有默认比较运算符;你必须创造一些东西。

编辑 2:比较函数的目的
std::set 的比较函数参数允许您为 Hallo 结构指定比较函数。它允许您创建一个由成员one 订购的集合,您可以创建另一个由two 成员订购的集合。

此外,您可以为升序或降序定义排序函数。选择很丰富。

【讨论】:

  • C++20 将提供一种使类默认可比较的好方法:en.cppreference.com/w/cpp/language/default_comparisons
  • std::set 不使用operator&lt; 您的回答可能会产生误导。
  • @Slava:std::set 如何使用整数比较?我一直认为它使用了&lt; 运算符。
  • 它使用std::less,默认使用operator&lt;。您的答案可以简化,但有时 std::less 和直接使用 operator&lt; 的区别很重要。引用“如果特化 std::less 的函数调用运算符调用一个内置运算符比较指针,即使内置运算符,它也会产生严格的全序。 "
【解决方案3】:

任何数学集合的成员都是唯一的;没有两个可以拥有相同的密钥。但是对于像struct 这样的用户定义类型,相同 意味着什么?我们知道两个字符串相等或两个整数意味着什么。但是你的结构的“相同”的含义取决于你,并由你来定义。

根据定义,std::set 的成员都是唯一且已排序的。因此,除了强制唯一性之外,std::set 按顺序代表成员。什么顺序?同样,对于用户定义的类型,由用户定义一个对象“小于”另一个对象的含义。

这就是operator&lt; 的用武之地。您可以定义其中一个对象小于另一个对象的含义。 std::set 在您的用户定义类型上调用您的用户定义运算符,并将成员按定义的顺序排列。它还使用该函数来强制唯一性:如果一个新项目不能在现有项目之前或之后插入,它等于它,并且插入被拒绝。

【讨论】:

    猜你喜欢
    • 2021-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-23
    • 1970-01-01
    • 2023-02-02
    • 1970-01-01
    相关资源
    最近更新 更多