【问题标题】:Why can't I overload this comparator that I pass to std::upper_bound为什么我不能重载传递给 std::upper_bound 的比较器
【发布时间】:2017-02-14 07:54:31
【问题描述】:

我对 C++ 还很陌生,我认为重载函数总是可以的,我的意思是重载:

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

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

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

我在this post

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

  1. 为什么代码不能编译
  2. 我如何编写 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;
}

【问题讨论】:

    标签: c++ c++11 stl


    【解决方案1】:

    您调用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;
    }
    

    【讨论】:

    • 好的,这回答了1。有没有办法回答2?
    • 您可以将它们包装在可调用对象中 - 仿函数或 labmda。
    【解决方案2】:

    因为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 &lt; x.first;

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

    【讨论】:

    • 太棒了!我喜欢最后一个选项。我有一个“实用程序”文件,据我了解,我几乎可以用类或结构替换“命名空间”,使其成为实用程序类,它会起作用!我这样做是因为stackoverflow.com/questions/8226489/…
    • namespaces 通常对实用程序进行分组更有意义,但是是的,这就是您可能选择类(具有非static 成员函数)的原因之一。
    猜你喜欢
    • 2022-01-18
    • 2017-08-30
    • 1970-01-01
    • 1970-01-01
    • 2011-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多