Consider the following code:
#include <vector>
#include <set>
using Iterator = std::vector<int>::iterator;
using ConstIterator = std::vector<int>::const_iterator;
std::set<ConstIterator> s;
int main() {
bool equal = Iterator{} == ConstIterator{};
s.count(Iterator{});
s.count(ConstIterator{});
s.contains(Iterator{});
s.contains(ConstIterator{});
}
This code is correct since ConstIterator
can be created from Iterator
, but not vise versa. If we change s
type to std::set<Iterator>
function calls that use ConstIterator
will fail the build.
As you know, both functions count
and contains
don't modify the collection, and internally they just do compare passed value with stored one. And that comparison is absolutely correct (see equal
part from code example), we can compare for equality Iterator
and ConstIterator
. One possible workaround is to use find_if
with lambda instead, e.g.:
std::ranges::find_if(s, [](auto v) { return v == ConstIterator{}; });
Hence, the question: how can we deal with count
/contains
in standard and beautiful way when we have ConstIterator
and want to look into container where Iterator
s are stored?
Use std::set<Iterator, std::less<>>
, or std::set<Iterator, std::ranges::less>
if you can use that.
These comparator types include an is_transparent
member, which signals to std::set
that it should support comparisons with any key-comparable type instead of converting to the key type. (The reason for the need to opt in is backward compatibility).
These types themselves also accept differing types in their comparison operator, as opposed to something like std::less<int>
(the default for std::set<int>
), which would specifically have operator<(const int&, const int&)
.