Reputation: 50774
I have several std::map<some_type, some_other_type>
and I'm trying to write a function template Lookup
as shown below.
The function template works fine when the key is a pointer or a scalar, but if the key is std::string
there are problems.
#include <iostream>
#include <map>
// Usage :
// bool valueisinmap = Lookup(themap, thekey, thevalue);
template <typename TK, typename TV>
bool Lookup(std::map<TK, TV>& map, TK key, TV& value)
{
auto it = map.find(key);
if (it != map.end())
{
value = it->second;
return true;
}
else
{
return false;
}
}
int main()
{
std::map<std::string, std::string> m;
m.insert(std::make_pair("2", "two"));
std::string x;
std::string key = "2";
if (Lookup(m, key, x))
std::cout << "OK\n";
if (Lookup(m, "2", x)) // problem here
std::cout << "OK\n";
}
I understand why Lookup(m, "2", x)
doesn't compile because the type of "2"
is not std::string
but is there a way to write the function template so I can use Lookup(m, "2", x)
as well as Lookup(m, key, x)
, key
being a std::string
?
And if yes this raises a second question:
bool Lookup(std::map<TK, TV>& map, TK key, TV& value)
key
is passed by value and if the type of key
is std::string
, a copy is made. Is there a way to pass key
by reference (or some C++14 and plus magic) and still being able to use Lookup(m, "2", x)
?
Upvotes: 3
Views: 96
Reputation: 172924
You can introduce another template parameter for key
as @Ton van den Heuvel answered, another way is to exclude it from template argument deduction:
template <typename TK, typename TV>
bool Lookup(std::map<TK, TV>& map, const std::type_identity_t<TK>& key, TV& value)
Then TK
will be only deduced from the 1st parameter map
; if you pass a const char[]
to the function as key
it'll be converted to std::string
then passed as the argument. And you can make is pass-by-const-reference to avoid potential unnecessary copy.
BTW: std::type_identity
is supported from C++20; if your compiler doesn't support it, you can make your own easily.
template<typename T> struct type_identity { typedef T type; };
Upvotes: 3
Reputation: 10528
One way to solve this is to introduce a separate type parameter for the key type, as follows:
template <typename TKM, typename TK, typename TV>
bool Lookup(const std::map<TKM, TV>& map, const TK& key, TV& value)
{
auto it = map.find(key);
if (it != map.end())
{
value = it->second;
return true;
}
else
{
return false;
}
}
As long as TK
is implicitly convertible to TKM
, Lookup
can be called with a key of type TK
in conjunction with a map that has key type TKM
.
Upvotes: 3
Reputation: 38277
You need two things here. First, the key type can be deduced seaparately (typename K
below). Second, you want to pass the key as const
-qualified reference and setup the map with a C++14 transparent comparison function (typename Comp
below) to avoid unnecessary copies (see this thread for details about transparent comparators).
template <typename TK, typename TV, typename Comp, typename K>
bool Lookup(const std::map<TK, TV, Comp>& map, const K& key, TV& value)
{
// Same as before...
}
std::map<std::string, std::string, std::less<>> m;
Specifying std::less<>
as the std::map
comparison type makes sure that overloads #3 and #4 of std::map::find
are available.
Note that I have additionally const
-qualified the map parameter itself, as the Lookup
template doesn't modify it.
Upvotes: 1