为什么我不能重载我传递给 std::upper_bound 的比较器

Why can't I overload this comparator that I pass to std::upper_bound

本文关键字:upper 比较器 bound 重载 不能 为什么 std      更新时间:2023-10-16

我对C++相当陌生,认为重载函数总是可以的,我所说的重载是指:

C++中的函数重载您可以在同一范围内对同一函数名进行多个定义。函数的定义必须因参数列表中参数的类型和/或数量而异。不能重载仅因返回类型不同而不同的函数声明。

然而,当我编写下面的代码时,它不会编译,我的印象是std::upper_bound无法解析它应该使用哪个比较器,尽管这看起来很容易,因为只有一个具有正确签名。

EDIT这些函数位于命名空间中的实用程序文件(而非类)中。

我在这篇文章之后做了这件事

我可能完全错了,你能解释一下吗

  1. 为什么代码不编译
  2. 我如何编写lt的两个实现,哪个不同的签名将使代码可编译和运行
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <algorithm>
//I have a utility file where functions are defined in a namespace
namespace util{
bool lt(double y, const std::pair<double, long>& x) { 
return x.first < y;
}
//If this function is commented the whole will compile and run
bool lt(const std::pair<double, long>& x, double y) {
return x.first < y;
}
}
int main()
{
std::vector<std::pair<double,long> > v;
v.push_back(std::make_pair(999., 123));
v.push_back(std::make_pair(100., 3));
v.push_back(std::make_pair(15., 13));
v.push_back(std::make_pair(10., 12));
v.push_back(std::make_pair(1., 2));
//use upper_bound
std::vector<std::pair<double,long> >::iterator it =
std::upper_bound(v.begin(), v.end(), 25., util::lt);
std::cout << " it->first => " << it->first << std::endl;
}

调用upper_bound的地方不是lt的调用站点,因此这里没有进行过载解决,并且您的过载是不明确的。

#include <algorithm>
#include <cmath>
#include <iostream>
#include <string>
#include <vector>
bool lt(const std::pair<double, long>& x, const std::pair<double, long>& y)
{
return x.first < y.first;
}
int main()
{
std::vector<std::pair<double, long>> v;
v.push_back(std::make_pair(999., 123));
v.push_back(std::make_pair(100., 3));
v.push_back(std::make_pair(15., 13));
v.push_back(std::make_pair(10., 12));
v.push_back(std::make_pair(1., 2));
// use upper_bound
std::vector<std::pair<double, long>>::iterator it =
std::upper_bound(v.begin(), v.end(), std::make_pair(25., 0.), lt);
std::cout << " it->first => " << it->first << std::endl;
}

因为std::upper_bound是谓词类型(您正在传递的函数)的模板,所以在查看任何upper_bound主体之前,必须知道要使用的特定重载(这是可以执行重载解析的地方,因为参数是已知的)。有各种各样的方法来解决这个问题,没有一种特别漂亮:

  • 转换为确切类型:

    std::upper_bound(v.begin, v.end(), 25.0, static_cast<bool (*)(double, const std::pair<double, long>&>(lt))
    
  • 用lambda:包装调用

    [](auto x, auto y){ return lt(x, y); }
    
  • 将函数放入一个类中,以便使用类的类型实例化模板,然后在模板的主体中进行重载解析:

    struct LT {
    bool operator()(double y, const std::pair<double, long>& x) {
    return x.first < y;
    }
    bool operator()(const std::pair<double, long>& x, double y) {
    return x.first < y;
    }
    };
    // ...
    std::upper_bound(v.begin(), v.end(), 25.0, LT());
    

注意,后两个选项几乎是一样的!


事实上,我建议你停下来想想你在做什么。在两种完全不同的类型之间提供订购真的有意义吗?你的第一个函数实际上似乎是实现gt(大于),也许它应该是return y < x.first;

我还真的认为使用endl是一种糟糕的做法(我知道不是每个人都同意我的观点)。