I'd like to use SFINAE (with void_t) to determine whether a class template specialization or instantiation has a certain member type defined. However the primary class template has a static_assert
in it's body.
Is it possible to retrieve this information without modifying the primary template, and without preprocessor tricks?
#include <type_traits>
template <class T>
struct X {
static_assert(sizeof(T) < 0, "");
};
template <>
struct X<int> {
using type = int;
};
template <class...>
using void_t = void;
template <class, class = void_t<>>
struct Has : std::false_type {};
template <class T>
struct Has<T, void_t<typename T::type>> : std::true_type {};
int main() {
static_assert(Has<X<int>>::value == true, "");
// How to make this compile?
static_assert(Has<X<char>>::value == false, ""); // ERROR
}
X<int>
is an explicit specialization, though X<char>
is an implicit instantiation of the primary template. And when the compiler creates this instantiation then the entire SFINAE machinery is stopped with an error caused by the static_assert
declaration inside the primary template body.
My concrete motivation is this: I am creating a generic wrapper class template and I'd like to define a hash function for it if it's template type parameter has a specialization for std::hash<>
. However gcc 4.7 puts a static_assert
inside the definition of the primary template of std::hash<>
. The libstdc++ of gcc 4.8 and llvm's libc++ just simply declare the primary template. Consequently my class template does not work with gcc/libstdc++ 4.7.
// GCC 4.7.2
/// Primary class template hash.
template<typename _Tp>
struct hash : public __hash_base<size_t, _Tp>
{
static_assert(sizeof(_Tp) < 0,
"std::hash is not specialized for this type");
size_t operator()(const _Tp&) const noexcept;
};
// GCC 4.8.2
/// Primary class template hash.
template<typename _Tp>
struct hash;
This problem is similar to this one, but I am not satisfied with the accepted answer there. Because here we cannot "inject 'patches' to to match the static_assert" since the assertion will always fail once the primary template is instantiated with any type parameter.
EDIT: Above I describe my concrete motivation to provide context for the abstract problem which is clearly stated upfront. This concrete motivation refers gcc 4.7, but please try to be independent from the libstdc++ 4.7 implementation details in comments and answers. This is just an example. There can be any kind of C++ libraries out there which might have a primary class template defined similarly to X
.