【问题标题】:Can I legally use a struct with overloaded operator() as Compare for std::upper_bound?我可以合法地使用重载 operator() 的结构作为 std::upper_bound 的比较吗?
【发布时间】:2016-03-15 09:39:43
【问题描述】:

我有这样的结构(类型被简化以延续这一点),生活在std::vector

struct Region {
    int first;
    int count;
    struct Metadata region_metadata;
};

在向量中,它们按first 排序。如果添加firstcount,则得到下一个区域的first;所以基本上这个结构向量描述了连续数字范围的元数据。

现在给定一个整数,我想查找元数据。对区域进行排序后,我可以使用std::upper_bound。我是这样实现的:

struct Comp
{
    inline bool operator()(const Region &region, int index) const
    {
        return region.first < index;
    }

    inline bool operator()(int index, const Region &region) const
    {
        return index < region.first;
    }
};

这有效,当调用std::upper_bound 时:

auto iter = std::upper_bound(m_regions.begin(),
                             m_regions.end(),
                             index,
                             Comp());

现在这恰好可以工作,因为upper_bound 可以在内部选择符合其要求的重载,因为它同时调用Comp()(Region, int)Comp()(int, Region)(这就是[](const Region &amp;reg, int index){…} 不起作用的原因)。

我实际上是通过在使用我之前提到的 lambda 时跟踪错误消息来提出解决方案的。 docs for std::upper_bound at cppreference.com写关于第四个论点:

比较函数对象(即满足 如果第一个参数是,则返回 ​true 小于秒。

比较函数的签名应该等同于 以下:

bool cmp(const Type1 &a, const Type2 &b);

签名不需要有const &amp;,而是函数对象 不得修改传递给它的对象。 Type1Type2 类型 必须使得T 类型的对象可以隐式转换为 Type1Type2 都可以,ForwardIt 类型的对象可以是 取消引用,然后隐式转换为Type1Type2

Type1 类型必须使得T 类型的对象可以 隐式转换为Type1Type2 类型必须是这样的 ForwardIt 类型的对象可以被取消引用,然后隐式 转换为Type2。​

(cppreference has been fixed 因为我发布了这个问题,谢谢@T.C.)

这里,Tstd::upper_bound 的第三个参数,ForwardIt 是前两个参数的类型。这句话没有提到函数对象实际上是一个重载其operator() 以涵盖“正向”和“反向”情况的结构。

所以在写规则中,这是合法的,还是我的特定编译器/标准库组合 (g++ 5.3.1) 的产物?

我对 C++14 或 C++17 的特定答案感兴趣。

完整示例:

#include <algorithm>
#include <iostream>
#include <vector>


struct Region {
    Region(int first, int count, int n):
        first(first),
        count(count),
        n(n)
    {

    }

    int first;
    int count;
    int n; // think struct Metadata
};


struct Comp
{
    inline bool operator()(const Region &region, int index) const
    {
        return region.first < index;
    }

    inline bool operator()(int index, const Region &region) const
    {
        return index < region.first;
    }
};


int main() {
    std::vector<Region> regions;
    regions.emplace_back(0, 10, 1);
    regions.emplace_back(10, 10, 2);
    regions.emplace_back(20, 10, 3);

    const int lookup = 10;

    auto iter = std::upper_bound(
        regions.begin(),
        regions.end(),
        lookup,
        Comp());

    // yes, I omitted error checking here, with error being iter == regions.begin()
    std::cout << lookup << " is in region with n = " << (iter-1)->n << std::endl;
}

【问题讨论】:

  • 啊,@T.C.,这也解释了我的困惑:-)。谢谢。

标签: c++ c++11 stl language-lawyer


【解决方案1】:

现在这恰好可以工作,因为upper_bound 可以在内部选择 符合其要求的重载,因为它同时调用 Comp()(Region, int)Comp()(int, Region)(这就是 [](const Region &amp;reg, int index){…} 不起作用的原因)。

不,upper_bound 只调用Comp 的第二次重载。这正是您的(固定 - 感谢@T.C.!)引用的内容:比较器的第一个参数始终是upper_bound 的第三个参数。应该交换 lambda 的参数。

upper_bound/lower_bound 的比较器中重载 operator() 本质上是没有意义的,因为这些算法只会选择一个重载。

operator() 应该像您在使用 equal_range 时所显示的那样被重载,并且这样做是合法的,因为比较器(或与此相关的任何函子)的内部细节与库无关:您只需要确保排序是严格的(即语义正确)并且重载是明确的。

【讨论】:

  • 你说的很对。这可能会使这个问题偏离主题。我弄乱了 lambda 的参数并假设它们是正确的,所以我添加了重载,这当然使它工作。但是由于我的假设不正确,因此结论也是错误的;只要 (int, Region) 重载,它就会起作用(并且确实起作用)。
  • 问题仍然存在。我们是否可以依赖算法函子的重载解析,即 应该 这适用于equal_range
  • @sp2danny 够公平的。
猜你喜欢
  • 1970-01-01
  • 2019-06-09
  • 2023-02-02
  • 2017-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-28
  • 1970-01-01
相关资源
最近更新 更多