BACKGROUND
I am trying to write a class template Hasher which will be implemented in two different ways depending on whether or not std::hash<T> has been implemented for T:
template<typename T>
struct Hasher
{
std::size_t hash( T t ) const;
// implement as A { std::hash<T> h; return h( t ); }
// or B { std::hash<std::string> h; return h( t.to_string() ); }
};
If std::hash<T> has been specialised, I want to use it. If not, I expect T to have a to_string() function to return a key for me to hash on.
For example, according to cppreference, if T is long long, a pointer, or std::string, I want version A. If it is not one of those standard ones listed and if the user has not specialised std::hash<T> for his own type, I expect T to have a std::string to_string() const for me to call. In this case, I want to generate version B.
PROBLEM
How do I use C++11/type_traits/no-SFINAE to generate the proper implementation?
ADDENDUM
Another way to think about it:
It's almost like I want version B to be the default behavior (ie, if no specialization exists, to use version B).
TESTED NAWAZ'S SOLUTION
I just tried out Nawaz's solution on gcc 4.8.1 as his came in first and is actually the easiest for me to read and understand (more important).
#include <functional>
#include <cassert>
template<typename T>
class Hasher
{
// overloading rules will select this one first... ...unless it's not valid
template<typename U>
static auto hash_impl(U const& u, int)
-> decltype(std::hash<U>().operator()( u ))
{
return std::hash<U>().operator()( u );
}
// as a fallback, we will pick this one
template<typename U>
static auto hash_impl(U const& u, ... )
-> std::size_t
{
return std::hash<std::string>().operator()(u.to_string());
}
public:
auto hash( T t ) const -> decltype( hash_impl(t,0) )
{
return hash_impl( t, 0 );
}
};
struct Foo
{
std::string m_id;
std::string to_string() const { return m_id; }
};
int
main( int argc, char** argv )
{
std::string s{ "Bar" };
Foo f{ s };
long long l{ 42ll };
Hasher<long long> hl;
Hasher<Foo> hf;
Hasher<std::string> hs;
assert( hl.hash( l )==l );
assert( hf.hash( f )==hs.hash( s ));
return 0;
}
TESTED DANIEL FREY'S SOLUTION
Daniel's implementation is also very interesting. By computing first whether or not we have a hash, I am able to use tag-dispatch to select the implementation I want. We have a nice pattern/separation-of-concerns which leads to very clean code.
However, in the implementation of has_hash<>, the arguments to decltype confused me at first. In fact, it shouldn't read as arguments. Rather, it is a list of expressions (comma separated expressions). We need to follow the rules as expressed here.
C++ ensures that each of the expressions is evaluated and its side effects take place. However, the value of an entire comma-separated expression is only the result of the rightmost expression.
Also, the use of void() was a mystery to me at first. When I changed it to double() to see what would happen, it was clear why it really should be void() (so we don't need to pass in that second template parameter).
#include <functional>
#include <cassert>
template< typename, typename = void >
struct has_hash
: std::false_type {};
template< typename T >
struct has_hash< T, decltype( std::hash< T >()( std::declval< T >() ), void() ) >
: std::true_type {};
template<typename T>
class Hasher
{
static std::size_t hash_impl(T const& t, std::true_type::type )
{
return std::hash<T>().operator()( t );
}
static std::size_t hash_impl(T const& t, std::false_type::type )
{
return std::hash<std::string>().operator()(t.to_string());
}
public:
std::size_t hash( T t ) const
{
return hash_impl( t, typename has_hash<T>::type() );
}
};
struct Foo
{
std::string m_id;
std::string to_string() const { return m_id; }
};
int
main( int argc, char** argv )
{
std::string s{ "Bar" };
Foo f{ s };
long long l{ 42ll };
Hasher<long long> hl;
Hasher<Foo> hf;
Hasher<std::string> hs;
assert( hl.hash( l )==l );
assert( hf.hash( f )==hs.hash( s ));
return 0;
}