1

我经常看到人们只覆盖 operator<,而不是 > 或 ==。这是否意味着默认情况下, operator> 和 operator== 是使用 operator< 实现的?

我也经常看到人们写作(见这里

bool operator() (Node const& n1, Node const& n2) const
{
    // TODO: your condition
    return n1.a < n2.a;
}

那么 operator() 在这里是什么意思呢?这似乎非常违反直觉。

4

5 回答 5

4

他们只是覆盖的原因<是因为默认情况下这是有序容器用来比较值的,所以这就是他们需要定义来回答问题的全部内容。

#include <set>

struct my_fancy_integer
{
    int fancy;
};

// This is all std::set (or any ordered container) needs by default,
// So for an example answer I won't do anything else (as I expect you to
// learn and understand *why* it needs this by default).
bool operator<(const my_fancy_integer& first, const my_fancy_integer& second)
{
    return first.fancy < second.fancy;
}

// But I should really also defined the other comparison operators...
// For example, without operator> defined this would fail:
//
// std::set<my_fancy_integer, std::greater<my_fancy_integer>> x;
//
// But since you read documentation for std::set and std::greater you
// understand why this fails: std::set will use std::greater to order
// the values, and std::greater (by default) will try to use operator>.

int main()
{
    std::set<my_fancy_integer> x; // okay
}

不,其他运算符不是根据它隐式定义的(也不是根据其他任何东西)。在实际应用程序中,如果您定义了一个,则应该将它们全部定义。

或者,如果<在语法上对您的类型没有意义,但对它们进行排序仍然有价值,请定义一个可用的默认谓词,用户应将其传递给有序容器的谓词模板参数。

#include <set>
#include <string>
#include <tuple>

struct my_employee
{
    std::string name;
    int salary;
    int yearsEmployed;
};

// Saying one employee is "less" than another doesn't really make sense...
// But I can still give an *ordering* on them:
struct my_employee_ordering
{
    bool operator()(const my_employee& first, const my_employee& second) const
    {
        // I'll just reuse std::tuple's comparison operator, and tie the
        // fields of each structure into a tuple to use it. This orders
        // by name, salary, then yearsEmployed.
        return std::tie(first.name, first.salary, first.yearsEmployed) <
               std::tie(second.name, second.salary, second.yearsEmployed);
    }
};

int main()
{
    // We need to tell std::set how to order employees:
    std::set<my_employee, my_employee_ordering> x; // okay
}

operator()函数调用运算符。它允许您的对象被“调用”:

struct foo
{
    void operator()(int x) { std::cout << x << std::endl; }
};

foo f;
f(5); // calls foo::operator()(5)
于 2013-06-26T23:53:19.617 回答
2

首先,没有。< 的实现没有隐式定义 == 和 >。人们倾向于定义 <,因为标准库使用小于运算符专门比较列表排序和类似任务。

operator() 称为函数调用运算符。基本上,假设我有一个 struct foo 如下

struct foo {
    int operator()(int a, int b) {
        return a+b;
    }
};

现在,如果我有一个foo被调用的实例x,我可以使用x(6, 5)它,它将使用我提供的两个参数(在本例中为 6 和 5)调用函数调用运算符。函数调用运算符仅用于将结构视为函数,并且可以接受任意数量和类型的参数,甚至可以不接受任何参数。在您给出的示例中,当包含该函数的对象用作函数调用时,它将比较两个节点对象并true根据 < 运算符返回如果第一个小于第二个。

于 2013-06-26T23:56:14.887 回答
1

定义的最小比较或排序运算符是<and ==。其他比较运算符可以根据这些定义:

operator != -- !operator==
operator >= -- !operator<
operator <= -- operator== || operator <
operator >  -- !(operator== || operator <)

boost库包含将生成所有其他运算符的模板。有关示例,请参见“less_than_comparable”。

编辑1:
定义operator()排序操作​​,排序函数经常使用它。例如,您可以为升序定义一个函数,为降序定义另一个函数。要进行排序,您将传递升序函数对象或降序函数对象。

于 2013-06-26T23:54:04.720 回答
0

您所看到的是人们实现了专用函子而不是通用对象;这是 C++“让你做”但不“让你显式做”的情况。

因此,在不需要比较的有序容器中使用函数进行弱排序的情况下,您会看到“operator<”的重载。该类仅用于此目的,因此不需要实现比较运算符等。

operator() 用于谓词函子,以允许以清晰的方式“调用”对象:

struct EqualityPredicate {
    bool operator()(const Node& lhs, const Node& rhs) const { return lhs == rhs; }
};

EqualityPredicate& equality;
for (Node hay : haystack) {
    if(equality(hay, haystack))
        doWork(hay);
}

哪个是调用 equal.operator()(hay, haystack);

于 2013-06-27T00:01:28.573 回答
0

这是一项常见任务,您必须为对象重载/覆盖或定义自定义比较运算符,例如将它们存储在 sets/unordered_sets 中或将对象用作 maps/unordered_maps 中的键。为此,您必须定义“小于”运算符(<)、等于运算符(==)和“哈希”运算符。C++ 允许以不同的方式做到这一点。我首选的方法是在您的对象内部定义它们。因此,您可以更好地了解对象行为。我创建的示例在现实世界中可能没有多大意义,但展示了定制行为的想法。

#include<assert.h>
#include<set>
#include<string>
#include<unordered_map>
#include<unordered_set>
#include<map>

using namespace std;

struct Person
{
    string name;
    unsigned age;
    double wage;
    Person() :name(""), age(0), wage(0.0) {}
    Person(const string& n, unsigned a, double w) :name(n), age(a), wage(w) {}

    Person & operator=(const Person& p) {
        if (this == &p)
            return *this;
        this->name = p.name;
        this->age = p.age;
        this->wage = p.wage;
        return *this;
    }
    // less than oprator for sets
    bool operator<(const Person& other) const {
        return this->wage < other.wage;
    }
    // equal oprator for maps
    bool operator==(const Person& other)const {
        return ((this->name == other.name) && (this->age == other.age));
    }
    //hash operator for unordered_sets/unordered_maps
    size_t operator()(const Person& p) const {
        return std::hash<string>()(p.name);
    }
};

int main()
{
    set<Person> personsSet;
    Person a("a", 20, 3000.0), b("b", 30, 2000.0), c("c", 40, 1000.0), d("d", 25, 500.0), e("e", 31, 700.0);
    personsSet.insert(a);
    assert(personsSet.size() == 1);
    personsSet.insert(b);
    assert(personsSet.size() == 2);
    personsSet.insert(c);
    assert(personsSet.size() == 3);
    personsSet.insert(d);
    assert(personsSet.size() == 4);
    personsSet.erase(b);
    assert(personsSet.size() == 3);
    personsSet.erase(e);
    assert(personsSet.size() == 3);

    map<Person, string> personsMap;
    personsMap.insert({ a, "first" });
    personsMap.insert({ b, "second" });
    personsMap.insert({ c, "third" });
    personsMap.insert({ d, "fourth" });
    assert(personsMap.size() == 4);
    personsMap[d] = "";
    assert(personsMap[d] == "");
    personsMap.erase(b);
    assert(personsMap.size() == 3);
    personsMap.erase(e);
    assert(personsMap.size() == 3);

    unordered_set<Person, Person> personUset;
    personUset.insert(a);
    assert(personUset.size() == 1);
    personUset.insert(b);
    assert(personUset.size() == 2);
    personUset.insert(c);
    assert(personUset.size() == 3);
    auto f = personUset.find(b);
    personUset.erase(f);
    assert(personUset.size() == 2);
    f = personUset.find(e);
    assert(f == personUset.end());

    unordered_map<Person, int, Person> personUmap;
    personUmap[b] = 2;
    assert(personUmap.size() == 1);
    assert(personUmap[b] == 2);
    personUmap[c] = 3;
    personUmap[d] = 4;
    auto mf = personUmap.find(c);
    assert(mf->first == Person({"c", 40, 1000.0}));
    assert(mf->second == 3);
    assert(personUmap.size() == 3);
    personUmap.erase(mf);
    assert(personUmap.size() == 2);
    personUmap.erase(e);
    assert(personUmap.size() == 2);
}
于 2019-07-22T11:43:52.287 回答