binaryBigInt
binaryBigInt

Reputation: 1704

C++14: How can I use my template function with `iterator` and `const_iterator`

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

Answers (2)

R Sahu
R Sahu

Reputation: 206567

Couple of points:

  1. There is no need to for isEqualToEndOfMap to accept the iterator by reference.
  2. You may use a 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.

Update, in response to OP's comment.

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.

Option 2

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.

Option 3

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

catnip
catnip

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

Related Questions