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 Iterators are stored?
Use
std::set<Iterator, std::less<>>, orstd::set<Iterator, std::ranges::less>if you can use that.These comparator types include an
is_transparentmember, which signals tostd::setthat 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 forstd::set<int>), which would specifically haveoperator<(const int&, const int&).