【问题标题】:different compare signature for std::upper_bound and std::lower_boundstd::upper_bound 和 std::lower_bound 的不同比较签名
【发布时间】:2017-08-30 00:30:49
【问题描述】:

这里是 std::lower_boundstd::upper_bound 的示例,请注意将比较 lambda 的签名传递给它们 -

const auto lower_x = std::lower_bound(
          points.begin(), points.end(), rec.min_corner.x,

          [](const RankedPoint &rp, const double x) { return rp.point.x < x; });

const auto upper_x = std::upper_bound(
          points.begin(), points.end(), rec.max_corner.x,

          [](const double x, const RankedPoint &rp) { return x < rp.point.x; });

保持签名完全相反的可能原因是什么?当我使用 auto 而不是带有错误签名的明确类型时,我没有意识到这一点,并且 gcc 编译了(clang 没有)它。让我沮丧了 10 分钟。

【问题讨论】:

  • 因为比较函数的语义必须是“小于&lt;”。
  • Ranges 库将允许投影来解决这个问题 :-)

标签: c++ c++11 stl c++14 c++-standard-library


【解决方案1】:

lower_boundupper_bound 的自定义比较器版本是简单地使用&lt; 的概括。 lower_bound 产生不小于value 的第一个元素,因此发生的检查是elem &lt; value(或实际上是!(elem &lt; value))。 upper_bound 产生的第一个元素大于value,但我们不写elem &gt; value(这将需要operator&gt;),我们只是将排序翻转为value &lt; elem。这维护了operator&lt; 的唯一要求,但结果是参数的顺序颠倒了。

这概括了从elem &lt; valuecomp(elem, value)value &lt; elemcomp(value, elem)

最终,我们在设计时可以做出两种选择:我们可以在任何地方使用 same 比较器,但是对于某些算法,参数的顺序是相反的。或者,我们可以为每个算法使用不同的比较器,这取决于对该特定算法有意义的情况。在任何地方使用相同的比较器有很多优点 - 您只需使用相同的比较器:

std::vector<int> vs = ...;
std::sort(vs.begin(), vs.end(), std::greater<>{});
auto lo = std::lower_bound(vs.begin(), vs.end(), 5, std::greater<>{});
auto hi = std::upper_bound(vs.begin(), vs.end(), 5, std::greater<>{});

到处都是相同的比较器,代码看起来正确,并且做正确的事。如果我们颠倒upper_bound() 传递给它的比较器的参数顺序,我们就必须传入std::less&lt;&gt;{}。这只是......看起来不对。


您可能会对Ranges TS 感兴趣,它通过可调用的投影解决了这个问题。

【讨论】:

  • 写自己的投影就是template&lt;class F&gt; auto projected_order( F&amp;&amp; f ) { return [f=std::forward&lt;F&gt;(f)](auto&amp;lhs, auto&amp;rhs)-&gt;bool{ return f(lhs) &lt; f(rhs); }; }的全部,覆盖了90%的用例。当然,overload 也有帮助,但这需要更多代码。
猜你喜欢
  • 2014-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-18
相关资源
最近更新 更多