Imagine a generic class that implements a hashed set. It's signature would be something like:
template <class Key, class Hash = hash<Key>>
class hash_set;
Unless the user doesn't specify a hashing function explicitly the library uses a specialization of its 'hash' class. One such class would look something like:
template <class T>
struct hash
{
size_t operator () (const T& x) const {
return ???;
}
};
Suddenly a problem occurs - we don't know how to hash every possible type. We'll just have to go with specializations and leave the unspecialized class undefined so that a compile-time error is raised if the hash doesn't know how to do so.
template <class T> struct hash;
Now, a concise library writer would provide specializations for the hash class for some basic types, so that the hash_set is immediately useful for simple cases.
template <> struct hash<char> {...}
template <> struct hash<short> {...}
template <> struct hash<int> {...}
template <> struct hash<char*> {...}
template <> struct hash<std::string> {...}
template <> struct hash<etc.> {...}
Now, our user can specialize the hash class for custom types and thus extend the library's reach.
As our users happily trek along different instantiations of the hash_set template suddenly a calamity strikes! Our user tries to instantiate a hash_set<const int>. Of course the compiler complains that no such specialization of hash<> exists. The user is discontent, the library vendor is in a stupor. A common scenario, really.
The tedious solution in such cases is to write a specialization for both const and non-const types. Yuck.
Luckily for us Boost provides a nice feature in its TypeTraits library - remove_const. Using it we can rewrite our hash_set as follows:
template <class Key, class Hash = hash<typename boost::remove_const<Key>::type>>
class hash_set;
Excellent. However, if our library vendor ever wants to write another class that uses a hash, for example a hash_map, they have to remember to add the remove_const bit. Each time. And what if our const remover happens to change?
Being smart about code responsibilities we come up with the following solution - the const remover will become a part of the hashing library, instead of the container library:
template <class T>
struct select_hash {
typedef hash<typename boost::remove_const<T>::type> type;
};
or even better:
template <class T>
struct select_hash
: hash<typename boost::remove_const<T>::type>
{};
Thus usage becomes:
template <class Key, class Hash = select_hash<Key>>
class hash_set;
Now we're set for clear sailing. Yet there is a way to be even cleverer, thus removing the need of select_hash and relying solely on hash. Observe as we change our unspecialized template:
template <class T>
struct hash
: hash<typename boost::remove_const<T>::type>
{};
Now each instantiation of hash<> either stumbles on a perfect specialization (like hash<int>), or the generic one, that first tries to strip the const qualifier. Thus, a hash<const int> inherits hash<int> and it's a perfect circle. When used with a type that hash has no specialization for, the compiler raises an error that 'a class cannot inherit from itself'. We're done here.
Note: to remove the volatile qualifier as well use boost::remove_cv<>. Depending on your situation this may or may not be a useful move.