2

I have a specific search function. Because it's used on both std::set and std::map, it was duplicated in our code (not using template).

I have to maintain those two functions, and I'd like to move them to a single function using a template (and then have only one search preocedure to maintain).

I can't find how to convert an iterator to the container's value_type. For std::set, you just need to dereference the iterator (*iter), but for std::map, you need to access the second item of the iterator (which is a pair) iter->second.

Here is an isolated example:

template <class Container, class Object> bool MyFindFunction( const Container& container, Object& found )
{
    Container::const_iterator iter = container.begin();
    // do my special search procedure here
    // note that this code also needs to access the iterator's value...

    if ( iter != container.end() )
    {
        found = *iter; // this works for set, but not for map
        found = iter->second; // this works for map, but not for set
        // HOW TO MAKE IT WORK FOR BOTH??
        return true;
    }
    else
    {
        return false;
    }
}

int main ()
{
    std::set<double> mySet;
    std::map<int,double> myMap;
    double found = 0;

    MyFindFunction( mySet, found );
    MyFindFunction( myMap, found );
}

Note that the special search procedure also needs to access the value_type (or mapped_type for maps), so, moving this procedure to a template function and having a MyFindFunctionInMap and MyFindFunctionInSet function handling iterator to value conversion after calling the search procedure function won't help.

PS: Sorry, I'm using C++98..

3
  • Just create an access function using the templated iterator and implement it for map/set iterator. So you dont have the complete function implementedtwice, just the problematic access Commented Jun 16, 2015 at 8:34
  • Pedantic point: the value_type of a std::map<A, B> is std::pair<const A, B>, which is exactly what dereferencing an iterator gives you. The type you're looking for is mapped_type. Commented Jun 16, 2015 at 9:52
  • Yes, I'm looking for mapped_type, which is not defined for sets...this one only has value_type.... Commented Jun 16, 2015 at 9:54

3 Answers 3

3

You can use template function overload to distinguish these cases:

template <typename V>
inline V get_value(const V& v) { return v; }
template <typename K, typename V>
inline V get_value(const std::pair<K, V>& p) { return p.second; }

and then

found = get_value(*iter);

LIVE DEMO

Sign up to request clarification or add additional context in comments.

3 Comments

I like that, but it does not compile (I'm using VS2010). Apparently the map version function is not used by the compiler. Would there by a way to pass the get_value function to be used as a parameter?
@jpo38 My fault. I've updated my answer, pls check again.
It works great and is really a nice and easy way to fix the problem. Thanks!
1

You could use a boost::transform_iterator to build a set-like iterator from the map-like iterator.

Create an internal template function parameterized by a set-like iterator and only taking iterators, not the container, as arguments. Dispatch to this function either the original iterator or the transformed iterator.


Edit If you cannot use boost, build some functor to access your stuff:

#include <map>
#include <set>
#include <iostream>


using namespace std;


template<class Key, class Value>
struct access_key
{
    template<class Ref>
    const Key &operator()(const Ref &v) const
    {
        return v.first;
    }
};

template<class Key>
struct access_key<Key, Key>
{
    template<class Ref>
    const Key &operator()(const Ref &v) const
    {
        return v;
    }
};


template<class Container>
void fn(Container t)
{
    access_key<typename Container::key_type, typename Container::value_type> a;
    cout << a(*t.begin()) << endl;
}


int main()
{
    set<int> s;
    s.insert(1);

    map<int, int> m;
    m[1] = 1;

    fn(s);
    fn(m);

    return 0;
}

This accessor relies on the key type and value type being distinct/same for map/set.

5 Comments

@jpo38 See update. In the future, you might want to state restrictions in your question, BTW.
Sorry about that. access_key operator returns v.first, should it be v.second? Could you provide an example about how to use this access_key struct?
This gives access to the key, not the value, right? I adapted to value (changed v.first to v.second and return value from Key to Value but it does not work)
@jpos38 Just change first to second. The rest stays the same.
-2

One practical solution can be using extra boolean parameter to figure out whether container is map or not:

template <class Container, class Object> bool MyFindFunction( const Container& container, Object& found, bool isMap ){
   ..
   if(!isMap){
      found = *iter; // this works for set, but not for map
   }
   else{
      found = iter->second; // this works for map, but not for set
   }
..
}

1 Comment

This does not work. Even if only used with isMap set to true, compiler complains error C2440: '=' : cannot convert from 'const std::pair<_Ty1,_Ty2>' to 'double'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.