简体   繁体   中英

Template C++: How to access iterator value for both std::map and std::set?

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..

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

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.

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
   }
..
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM