Reputation: 6255
I have a hash table container called Map with the following method:
Value Map<Key, Value>::valueFor(const Key& key);
Unfortunately, the most used case is for Key = std::string
where we usually call the method with string literals such as:
const Value v = map.valueFor("my_key");
We loose a few cycles creating the std::string
. Therefore, I would like to add an overload
Value Map<std::string, Value>::valueFor(const char* key);
when Key = std::string
. I am sure that the compiler can even compute the hash at compile time with such a signature which would also help to speedup things.
Is there a way to do that in C++11
without template specializing the whole Map class and rewrite all the methods?
Upvotes: 1
Views: 149
Reputation: 67723
Just weaken your type requirements. Your valueFor
doesn't (need to) care what type the argument is, so long as the expression hash<Key>(arg)
is valid.
So, you can template valueFor
on its argument type, and just specialise your hash function and if necessary your key comparator.
eg. (untested, and using C++17 string_view
for brevity)
template <typename K>
struct Hasher
{
static size_t hash(K const &k) { return std::hash<K>()(k); }
};
template <>
struct Hasher<std::string>
{
static size_t hash(std::string const &s) {
return std::hash<std::string>()(s);
}
static size_t hash(std::string_view const &sv) {
return std::hash<std::string_view>()(sv);
}
static size_t hash(const char *cstr) {
return std::hash<std::string_view>()({cstr});
}
};
template <typename Key, typename Value>
template <typename KeyArg>
Value Map<Key,Value>::valueFor(KeyArg&& arg)
{
auto hash = Hasher<Key>::hash(std::forward<KeyArg>(arg));
// ...
}
Upvotes: 1
Reputation: 10939
You can just add another overload valueFor(char const * key)
. Probably you then also want to disable this overload with SFINAE if the Key
is not std::string
.
#include <iostream>
#include <string>
#include <type_traits>
template < typename Key, typename Value >
struct Map
{
Value valueFor(Key const& key)
{
std::cout << "valueFor(const Key& key)\n";
return Value{};
}
template < typename _Key = Key,
typename = typename std::enable_if< std::is_same < _Key, std::string >::value >::type >
Value valueFor(char const * key)
{
std::cout << "valueFor(char const * key)\n";
return Value{};
}
};
int main()
{
Map<std::string, int> map;
int v = map.valueFor("my_key");
Map<int, int> other_map;
//int v = other_map.valueFor("my_key"); // BOOM!
}
Upvotes: 1