Why doesn't std::ranges::contains try using member contains just like std::ranges::begin tries using member begin? Or does it?

huangapple go评论72阅读模式
英文:

Why doesn't std::ranges::contains try using member contains just like std::ranges::begin tries using member begin? Or does it?

问题

std::begin 可以调用参数具有的 .begin 成员函数。同样,std::ranges::begin 似乎也是如此。

然而,在 std::ranges::contains 页面上,我看不到单词“成员”或.contains的提及。为什么会这样?

我的意思是,如果我要编写 std::ranges::contains(someRange, someVal),如果someRange碰巧是std::mapsomeVal是其中的键,我真的希望它调用成员 contains

我还查看了Range-v3中的 ranges::contains,在我看来,它似乎只是在范围内进行线性搜索,并没有特殊处理支持成员 contains 的参数。

英文:

std::begin can call the .begin member function if the argument has it. Also std::ranges::begin seems to do the same.

However, at the page for std::ranges::contains I see no mention of the word member, nor of .contains. Why is that?

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.

I've also looked at ranges::contains from Range-v3, and it looks to me that too just makes a linear search through the range, with no special treatment for arguments supporting member contains.

答案1

得分: 5

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
}
英文:

> 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&lt;int, string&gt; 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&lt;int const, string&gt;, 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) -&gt; bool = default;
};
struct JustXs {
    auto operator()(Point a, Point b) const -&gt; bool {
        return a.x &lt; b.x;
    }
};

int main() {
    std::set&lt;Point, JustXs&gt; points = {Point{1, 2}};
    bool a = points.contains(Point{1, 3});               // true
    bool b = std::ranges::contains(points, Point{1, 3}); // false
}

答案2

得分: 2

ranges::begin 不是一个算法;它是一个在范围上执行的基本操作(实际上,std::ranges::range 概念的一部分定义是 ranges::begin 起作用)。ranges::begin 的整个目的是与范围进行交互。从机械的角度来看,ranges::begin 是一个定制点对象,任何想成为范围的个体类型都需要提供一定的接口。这就是这个对象的目的。

ranges::contains 是一个算法;它是可以应用于范围的操作,具有特定的含义和目的。算法不可定制,也不打算如此。如果特定范围有一个与算法同名的函数,那么从算法的角度来看,这只是巧合。算法不是调用范围接口的方式;它们是一种通过范围接口来操作范围、访问或操作它的方式。

不要将算法与定制点混淆。

英文:

ranges::begin is not an algorithm; it is a fundamental operation on a range (indeed, part of the definition of the std::ranges::range concept is that ranges::begin works). The entire purpose of ranges::begin is to interface with a range. Mechanically, ranges::begin is a customization point object for which an individual type that wants to be a range will provide a certain interface. That is the purpose of the object.

ranges::contains is an algorithm; it is a thing that can be done to a range, which has a specific meaning and purpose. Algorithms are not customizable, nor are they intended to be. If a particular range has a function that is named the same as an algorithm, then from the perspective of the algorithm, this is a coincidence. Algorithms are not a way to call a range's interface; they are a way to act on a range, to access or manipulate it through a range interface.

You should not confuse algorithms with customization points.

huangapple
  • 本文由 发表于 2023年3月10日 01:00:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/75687762.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定