kkm mistrusts SE
kkm mistrusts SE

Reputation: 5510

Template specialization and const qualifier on type

Why does the compiler expect separate specialization of a template on (concrete) types T and const T? Let me show an example. I had an unordered map keyed by a class type Key

std::unordered_map<Key, Value> data;

and for it to compile had to specialize std::hash on the type Key as

namespace std {
  template<>
  class hash<Key> { /* implementation */ };
}

However, when I changed the map type to

std::unordered_map<const Key, Value> data;

the compiler did not use my specialization and instead selected the generic std::hash<T>, which is little more than a compile time assert, until I specialized std::hash<const Key>.

Putting aside the utility of qualifying the map key type with const, why does not const T collapse to T when looking for a specialization in this case?

Also, could the template class std::hash (technically) be designed to allow such a collapse?

Upvotes: 4

Views: 609

Answers (1)

rici
rici

Reputation: 241671

I can't really answer the "why", since it was a decision of the standards committee, who no doubt had their reasons.

The issue is not restricted to custom types. You can't instantiate std::unordered_map<const std::string, int> either.

Of course, it is rarely if ever useful to use an explicit const type as the key type of a standard associative container, since the value_type of the container is std::pair<const Key, Val>; the key is const regardless of its declared type. But I understand that is not related to the original question, and the volatile qualifier would have had the same effect.

Could it have been otherwise? Sure. It's not even that difficult:

template<typename Key,
         typename Val,
         typename Hash  = std::hash<typename std::remove_cv<Key>::type>,
         typename KeyEq = std::equal_to<Key>,
         typename Alloc = std::allocator<std::pair<const Key, Val>>>
using my_unordered_map = std::unordered_map<Key, Val, Hash, KeyEq, Alloc>;

The only difference is the use of std::remove_cv in the default template argument for the Hash template parameter.

(Live on coliru.)

Upvotes: 3

Related Questions