当前位置: 首页 > 知识库问答 >
问题:

为什么重载操作符

墨星鹏
2023-03-14

以下是MWE:

#include <iostream>
#include <tuple>
#include <queue>
using namespace std;

bool operator<(tuple<int, int, int> lhs, tuple<int, int, int> rhs)
{
    return get<1>(lhs) < get<1>(rhs);
}

int main()
{
    priority_queue<tuple<int, int, int>> q;
    q.push(make_tuple(2, 5, 3));
    q.push(make_tuple(2, 3, 3));
    cout << get<1>(q.top());
    return 0;
}

奇怪的是,无论我键入


共有2个答案

令狐宜民
2023-03-14

std::tuple位于名称空间std,编译器将查找重载的运算符

修复:

#include <iostream>
#include <tuple>
#include <queue>
using namespace std;

bool operator<(tuple<int, int, int> lhs, tuple<int, int, int> rhs)
{
    return get<1>(lhs) > get<1>(rhs);
}

int main()
{
    using T = tuple<int, int, int>;
    priority_queue<T, vector<T>, bool(*)(T, T)> q(::operator<);
    q.push(make_tuple(2,5,3));
    q.push(make_tuple(2,3,3));
    cout << get<1>(q.top());
    return 0;
}
// Outputs: 3

百里秋月
2023-03-14

您的<code>运算符过载

搜索合适的重载发生在调用运算符的命名空间中,这是priority_queue所在的命名空间,即std。参数相关的查找会导致在参数的命名空间中进行额外的搜索,但因为tuple也在std命名空间中,这也没有帮助。编译器根本没有理由考虑全局命名空间。

相反,标准库对< code>std::operator的定义

// !!!!!!!!!!!!!!!!!!!!!!!!
// BAD SOLUTION, DO NOT USE
// !!!!!!!!!!!!!!!!!!!!!!!!

namespace std {
bool operator<(tuple<int, int, int> lhs, tuple<int, int, int> rhs)
{
    return get<1>(lhs) > get<1>(rhs); // Changed to > so we can see the difference.
}
}

现在输出为3。您的运算符现在被考虑并优先于默认运算符,因为非模板函数优先于模板函数。

但是标准禁止将自己的代码放入<code>名称空间std</code>(除了一些小的例外),那么该怎么办呢?解决方案是显式传递一个比较器函子:

#include <iostream>
#include <tuple>
#include <queue>
using namespace std;

struct element_1_greater {
    bool operator()(tuple<int, int, int> lhs, tuple<int, int, int> rhs)
    {
        return get<1>(lhs) > get<1>(rhs);
    }
};

int main()
{
    priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, element_1_greater> q;
    q.push(make_tuple(2,5,3));
    q.push(make_tuple(2,3,3));
    cout << get<1>(q.top()) << '\n';
    return 0;
}

请注意,我们还需要将第二个参数传递给priority_queue;当然,你可以用typedef或使用声明来缩短它。

 类似资料:
  • Kotin有一些固定数量象征性的操作符,我们可以在任何类中很容易地使用它们。方法是创建一个方法,方法名为保留的操作符关键字,这样就可以让这个操作符的行为映射到这个方法。重载这些操作符可以增加代码可读性和简洁性。

  • 这里你可以看见一系列包括操作符和对应方法的表。对应方法必须在指定的类中通过各种可能性被实现。 一元操作符 操作符 函数 +a a.unaryPlus() -a a.unaryMinus() !a a.not() a++ a.inc() a— a.dec() 二元操作符 操作符 函数 a + b a.plus(b) a - b a.minus(b) a * b a.times(b) a / b a.

  • 问题内容: 在深入研究之后,我发现Stream和Collector之间存在许多重复的逻辑,这些逻辑违反了不要重复自己的原则,例如:jdk-9和中的Stream#map&Collectors#mapping,Stream#filter&Collectors#filtering。等等 但自从溪流遵守告诉,不要问得墨meter耳的法则/ 得墨Law律和集热器遵守继承构成原则看来,这是合理的。 我只能想到

  • 我计划将我们的一个Spark应用程序迁移到Apache Flink。我试图了解它的容错特性。 我执行了以下代码,我看不到Flink实际上尝试重试任何任务(或子任务)。这可能会导致我丢失数据。我该怎么做才能确保每一次失败都能被Flink所覆盖? 我希望在屏幕上多次看到抛出异常消息。但是,当我使用fixedDelayRestart时,它似乎只是忽略了此消息,并为其他消息继续。

  • 请允许我提出一些抱怨,也许是令人厌烦但我想描述的是:“为什么会提出这个问题呢?”。我昨天晚上在这里、这里、这里回答的问题和其他人不一样。 Stream operations在做一些简单的工作时比Collector更有表现力,但是它们比Collector更慢,因为它会为每个操作创建一个新的Stream,并且Stream比Collector更繁重和抽象。例如: 收集器无法将短路终端操作作为流应用。例如

  • 你可以想象,Kotlin List是实现了数组操作符的,所以我们可以像Java中的数组一样访问List的每一项。除此之外:在可修改的List中,每一项也可以用一个简单的方式被直接设置: val x = myList[2] myList[2] = 4 如果你还记得,我们有一个叫ForecastList的数据类,它是由很多其他额外的信息组成的。有趣的是可以直接访问它的每一项而不是请求内部的list得到