Reputation: 21514
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..
Upvotes: 2
Views: 1439
Reputation: 172894
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);
Upvotes: 3
Reputation: 76297
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.
Upvotes: 1
Reputation: 54
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
}
..
}
Upvotes: -2