带lambda的std :: equal_range

时间:2015-10-30 04:26:41

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

假设我有一个字符串向量,我想找到所有以'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 < 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'} );

2 个答案:

答案 0 :(得分:4)

回答第一个问题,比较器是否可以使用通用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的两个重载可能是模糊的,你需要一个稍微不同的名称或其他方式(例如,一个标签)论证)消除两者的歧义。

答案 1 :(得分:4)

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(); });