I mean, if I was to write std::ranges::contains(someRange, someVal)
I would really want it result in a call to member contains
, if someRange
happened to be a std::map
and someVal
a key in it.
The problem is that these mean very different things. Given a std::map<int, string> m
, m.contains(42)
checks if the key 42
exists in the map, but ranges::contains(m, 42)
would check if 42
exists as a value in the range - but the value type is pair<int const, string>
, which isn't comparable to int
, so this wouldn't even compile.
The two algorithms simply do different things, so you can't have ranges::contains(m, v)
try to call m.contains(v)
. That would be correct (though unnecessary) for string
and string_view
. It could be correct and an improvement for set
but might be wrong (see below), but would be wrong for map
.
The same is true for other algorithms where the general algorithm is based on the whole value type but some specific containers apply the algorithm to just the key (e.g. find
, count
, etc.).
Trying to check if m.contains(v)
becomes a problem because these things in general tend to be very fragile in the presence of loose constraints (maybe the expression is valid because there's just no constraints on it, but there should be) or more flexible types (maybe some type just compares equal to everything, so the constraint is misleading - the canonical example here is using std::any
when considering rules for constructibility).
And even if all the constraints are even legit and pass in a sensible way, that still doesn't mean that using the member algorithm is correct. For example:
struct Point {
int x, y;
friend auto operator==(Point, Point) -> bool = default;
};
struct JustXs {
auto operator()(Point a, Point b) const -> bool {
return a.x < b.x;
}
};
int main() {
std::set<Point, JustXs> points = {Point{1, 2}};
bool a = points.contains(Point{1, 3}); // true
bool b = std::ranges::contains(points, Point{1, 3}); // false
}
contains
was added in C++20. ranges-v3 and its standard introduction all predate that. – Kottoranges::find
also don't use the containersfind
if it has it, so maybe it was just a missed optimization in the proposal. – Kottocontains
but maybefind
would have been a better choice in the first place, considering it is C++20, rather than C++23. – Divorce