Wednesday, August 30, 2023

Careful with std::ranges

<ranges>

  C++20 has added the the ranges library. Basically it works on ranges instead of iterators but added some subtle constraints to some algorithms. For example consider the lower_bound algorithm:

#include <algorithm>
#include <utility>

using IntPair = std::pair<int, int>;
   
IntPair a[1];

auto it = std::lower_bound(std::cbegin(a), std::cend(a), 1, [](const IntPair& r, int n) { return r.first < n; });

The lower_bound function only expects an asymmetric functor implementing the order between container and search element. To spare on typing out the begin and end iterator one could think to use the ranges library:

auto it = std::ranges::lower_bound(a, 1, [](const IntPair& r, int n) { return r.first < n; });

This gives though a ton of mystic error messages on VStudio:

1>error C2672: 'operator __surrogate_func': no matching overloaded function found
1>error C7602: 'std::ranges::_Lower_bound_fn::operator ()': the associated constraints are not satisfied
1>message : see declaration of 'std::ranges::_Lower_bound_fn::operator ()'

It turns out that the ranges variant expect a functor with all less combinations defined:

struct OpLess
{
   bool operator() (const int n1, int n2) const                 { return n1 < n2; };
   bool operator() (const IntPair& r1, const IntPair& r2) const { return r1.first < r2.first; };
   bool operator() (const IntPair& r, int n) const              { return r.first < n; };
   bool operator() (int n, const IntPair& r) const              { return n < r.first; };
};

auto it = std::ranges::lower_bound(a, 1, OpLess{});

Side note: concepts supposed to give more clearer error messages but are cryptic as well.

External links

Careful with std::ranges

<ranges>   C++20 has added the the ranges library. Basically it works on ranges instead of iterators but added some subtle constraint...