为什么 std::tuple 的重载运算符<在priority_queue中似乎不起作用?

MKC*_*CCT 15 c++ stdtuple

这是 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;
}
Run Code Online (Sandbox Code Playgroud)

奇怪的是,无论我输入<还是>在句子中return get<1>(lhs) < get<1>(rhs);,输出总是5. 为什么会出现这种情况?

Tho*_*mas 23

operator<未选择您的重载,因为它与std::priority_queue和位于不同的命名空间中std::tuple。它不在候选集中,因此它永远不会被视为过载候选者。

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

相反,使用标准库对元组的定义。std::operator<您可以看到,如果您将自己的实现放在一个namespace std块中:

// !!!!!!!!!!!!!!!!!!!!!!!!
// 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.
}
}
Run Code Online (Sandbox Code Playgroud)

现在输出是3. 您的运算符现在已被考虑优先于默认运算符,因为非模板函数位于模板函数之前。

但是标准禁止将自己的代码放入其中namespace std(除了一些小例外),那么该怎么办呢?解决方案是显式传递比较器函子:

#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;
}
Run Code Online (Sandbox Code Playgroud)

priority_queue请注意,我们还需要传递第二个参数;当然,您可以使用typedefor声明来缩短它。using