【问题标题】:std::equal_range with lambda带有 lambda 的 std::equal_range
【发布时间】:2015-10-30 04:26:40
【问题描述】:

假设我有一个字符串向量,我想找到所有以'a' 开头的字符串,所以我可以这样做:

struct cmp {
    bool operator()( const std::string &s, char c ) const { return s.front() < c; }
    bool operator()( char c, const std::string &s ) const { return s.front() < c; }
};
std::vector<std::string> strings;
...
std::sort( strings.begin(), strings.end() );
auto range = std::equal_range( strings.begin(), strings.end(), 'a', cmp{} );
...

这种方法容易出错,容易出错(比如我认为第二种方法应该是c &lt; s.front())并且有重复代码。

那么是否可以用泛型 lambda 而不是用 2 种方法的结构来实现比较函数?

更一般的问题,为什么要比较的值必须作为参数传递给std::lower_boundstd::upper_boundstd::equal_range,当它可以很容易地被 lambda 捕获或传递给比较结构时,那么这个问题就不会有吗?

如果std::equal_range 不需要值,它会如何工作?

struct cmp {
    cmp( char lc ) : c( lc ) {}
    bool operator()( const std::string &s ) const { return s.front() < c; }
    char c;
};
std::vector<std::string> strings;
...
std::sort( strings.begin(), strings.end() );
auto range = std::equal_range( strings.begin(), strings.end(), cmp{'a'} );

【问题讨论】:

  • 如果你愿意传递"a" 代替'a',那么一个接受两个字符串并比较它们的第一个字符的lambda 将起作用。否则,具有两个重载的命名类对我来说似乎是最干净的解决方案。但请注意,正如目前所写的,第二个重载的比较方式是错误的。
  • @IgorTandetnik 这很明显,但它可能不是字符串,而是一个对象,它按部分排序,所以我不想创建整个对象只是为了将它用作键。跨度>
  • 你可以使用std::partition_point
  • @T.C.你能提供一个例子作为答案吗?谢谢

标签: c++ lambda c++14 generic-lambda


【解决方案1】:

lower_bound

std::partition_point(strings.begin(), strings.end(),
                     [](const auto& s) { return s.front() < 'a'; });

upper_bound

std::partition_point(strings.begin(), strings.end(),
                     [](const auto& s) { return s.front() <= 'a'; });

是的,这意味着您必须编写两个调用才能获得 equal_range 的等价物。你可以把它包装成一个免费的模板:

template<class Iter, class T, class Proj>
std::pair<Iter, Iter> my_equal_range(Iter first, Iter last, const T& value, Proj proj) {
    auto b = std::partition_point(first, last, [&](const auto& s) { return proj(s) < value; });
    auto e = std::partition_point(b, last, [&](const auto& s) { return !(value < proj(s)); });
    return {b, e};
}

并将其称为

my_equal_range(strings.begin(), strings.end(), 'a',
               [](const auto& s) { return s.front(); });

Ranges TS 工作草案将预测添加到算法中,因此您将(最终)能够做到这一点:

std::experimental::ranges::equal_range(strings, 'a', {},
                                       [](const auto& s) { return s.front(); });

【讨论】:

  • 第二次std::partition_point 调用使用上一次调用的输出而不是first 会更有效吗?
  • @Slava 当然,这样会更有效率。
  • 您实际上可以重复使用firstlast :) first = ...; last = ...; return {first,last};
【解决方案2】:

要回答您的第一个问题,是否可以使用通用 lambda 实现比较器,是的,可以。在给定 charstring 参数的情况下,您还需要几个帮助函数来返回所需的结果。

auto get_char(char c)               { return c; }
auto get_char(std::string const& s) { return s.front(); }
auto cmp = [](auto const& l, auto const& r) { return get_char(l) < get_char(r); };

Live demo


您不能简单地让比较器捕获该值的一个原因是,equal_range 的两个重载可能会模棱两可,您需要稍微不同的名称或其他方式(例如,标签参数)来消除歧义两个。

【讨论】:

  • 很好的解决方案,但并不完美:(它有同样的问题阻止我在 lambda 之前使用算法 - 你必须在使用它的代码范围之外编写仿函数。
猜你喜欢
  • 2013-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多