Reputation: 1704
So I have these two functions which should make it more pleasant to check whether a key exists in a map:
template<template <typename...> class Map, typename K, typename T>
bool isEqualToEndOfMap(const typename Map<K, T>::iterator &it, const Map<K, T> &map) {
return it == map.end();
}
template<template <typename...> class Map, typename K, typename T>
bool isInMap(const K &key, const Map<K, T> &map) {
return isEqualToEndOfMap(map.find(key), map);
}
But unfortunately, map.find()
returns an std::map::const_iterator
which is incompatible with std::map::iterator
. I can't change to const typename Map<K, T>::const_iterator
because I want to use isEqualToEndOfMap
with regular iterators (non const), due to this:
const auto ret = std::find_if(mmap.begin(), mmap.end(),
[streamShm](const std::pair<int, StreamMapContainer> pair) {
return pair.second.pcktRefShm->id() == streamShm->id();
});
if(isEqualToEndOfMap(ret, mmap))
std::find_if
returns a regular iterator because map.begin()
and map.end()
are returning regular iterators. Sure, casting is possible. But it is even uglier:
std::map<int, StreamMapContainer>::const_iterator(..)
.
Upvotes: 1
Views: 71
Reputation: 206567
Couple of points:
isEqualToEndOfMap
to accept the iterator by reference.const_iterator
as the argument type in isEqualToEndOfMap
. An object of type iterator
can be used in a call to the function.template<template <typename...> class Map, typename K, typename T>
bool isEqualToEndOfMap(typename Map<K, T>::const_iterator it, const Map<K, T> &map) {
return it == map.end();
}
That should work in both use cases in your posted code.
In a comment, you wrote:
But even if is cheap, why not avoid it?
You have to explore your options and decide which one is the best for your needs.
In addition to what I already suggested, I can think of two more options.
Have couple of overloads, one for const_iterator
and one for iterator
.
template<template <typename...> class Map, typename K, typename T>
bool isEqualToEndOfMap(const typename Map<K, T>::const_iterator &it, const Map<K, T> &map) {
return it == map.end();
}
template<template <typename...> class Map, typename K, typename T>
bool isEqualToEndOfMap(const typename Map<K, T>::iterator &it, const Map<K, T> &map) {
return it == map.end();
}
This is not better than the first option since you have to maintain essentially duplicate code.
Use const_iterator&
as the argument type instead of const_iterator
.
template<template <typename...> class Map, typename K, typename T>
bool isEqualToEndOfMap(const typename Map<K, T>::const_iterator &it, const Map<K, T> &map) {
return it == map.end();
}
This saves you a copy when the function is called with a const_iterator
but still requires a copy, a temporary object, when it is called with an iterator
object.
You could potentially use this option but it is not idiomatic. Iterator objects are meant to be used almost as thought they are pointers and passed by value.
My recommendation would be to stick with the first option.
Upvotes: 3
Reputation: 25388
Why not two templates:
template<template <typename...> class Map, typename K, typename T>
bool isEqualToEndOfMap(const typename Map<K, T>::iterator &it, const Map<K, T> &map) {
return it == map.end();
}
and:
template<template <typename...> class Map, typename K, typename T>
bool isEqualToEndOfMap(const typename Map<K, T>::const_iterator &it, const Map<K, T> &map) {
return it == map.end();
}
Upvotes: 2