英文:
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::map
,someVal
是其中的键,我真的希望它调用成员 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<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
}
答案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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论