Firstly no, there is not an O(1)
operation to get a set of keys from a map. Constructing the set would have to be Omega(n)
.
However, you can do the set_intersection
you want directly on the map and the other range, whatever that is. Untested code:
template <typename InputIterator, typename Map, typename OutputIterator>
void map_set_intersection(InputIterator first, InputIterator last, const Map &m, OutputIterator o) {
std::set_intersection(
first, last,
m.begin(), m.end(),
o,
KeyCompare<typename Map::value_type, typename std::iterator_traits<InputIterator>::value_type>()
);
}
All the magic is in the KeyCompare type:
template <typename MapValue, typename SetValue>
struct KeyCompare {
bool operator()(const MapValue &lhs, const SetValue &rhs) {
return lhs.first < rhs;
}
bool operator()(const SetValue &lhs, const MapValue &rhs) {
return lhs < rhs.first;
}
};
std::set_difference
is defined to copy values from the first range specified, which in this case is the range defined by the iterators. It can do that provided that it can compare values in the first range with values in the second range in either order (which it can, thanks to KeyCompare
). The two ranges don't need to have the same type, so you don't need a set of keys from the map.
If you're already using the 6-parameter form of set_intersection
, then you need to change KeyCompare
so that after taking the key out of the map entry, it uses your comparator instead of <
.
If you're using a custom comparator or overloaded operator<
and the value type of the iterator range is the same as the value type of the map, then this doesn't work. KeyCompare
can no longer use the types to work out which order the arguments have been provided and hence which one it should extract the first
out of. I don't think this is a very common scenario, but I also don't see an escape from it using this approach. You'd need Crazy Eddie's solution, adapt the map iterator. Which amounts to this question: Iterate keys in a C++ map