c++iteratorconst-iterator

A way to use count/contains on map holding iterator by giving const_iterator


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?


Solution

  • 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&).