Ok, I don't claim that I habe understood all perfectly, myself, but I will try to answer to the best of my knowledge:
Because class is here a default template parameter that can represent any number of parameters independent of their type?
Nearly. This template will match any instantiation with one or two template arguments, so all of the form has_type_member<T>
or has_type_member<T, U>
. This is due to
class
matches any type. But this not special (you could also write class T
you just don't need the name, cause you don't reference it in the declaration) Every template at first matches all types and could only be differentiated by the number of arguments. Just often, we have either a constraint through some SFINAE-magic (like enable_if
) or we have a better fit by partial template specification later on.
class = void
matches every type, as above and also no type at all, since void
fills in if we have no argument.
We will only instantiate this template as has_member_type<T>
, so this will always be the first match, but maybe not the best match. But being the first match, it tells us: The second template argument has to be void
, since all further matches must either be a partial specification. Otherwise we would get ambiguity. Think, what would happen if the second template would give us int
if the expression is valid. Then we had two matches has_type_member<T, void>
and has_type_member<T, int>
, so which should we choose? That is why in the success case the type has to be void
and then this overload is also chosen, since it is more special.
Why would a valid expression be evaluated to void, and how does this help us? Also, why does the expression need to be valid to match void_t?
So the second part of the first question I already answered. Regarding the first: Think of the definition of void_t
:
template<class...>
using void_t = void;
So, ...
matches everything regardless of type and number, doesn't it? Actually it only match a valid type, if not how would it potentially use this type? (I know it doesn't use the type, but it has to be able to. And it cannot use an invalid type). Therefore it gives us void
if the template argument(s) passed are valid. So in our use case:
If T
has a member type T::type
, T::type
is a valid type and void_t<...>
matches it. So we get void_t<T::type>
at this point, which evaluates to void
, that fits to the primary but is more special, so we take it and get a true_type
.
What about if we have no type member? Then the expression T::type
is invalid, void_t<...>
cannot mazch it and hence the partial specification is invalid, so we cannot chose it, but this no problem, since Substitution Failure Is Not An Error, so we just go on with what we already found, the primary template.
Is it even necessary to say that class equals to std::void_t<> ? Wouldn't it be enough to write
template< class, class = void >
struct has_type_member : std::false_type { };
And if not, why?
Yes, it would, it is also done in the talk. void_t<>
is literally void
. I think the void_t
is only taken to be more consistent with the second specification. There void_t
is needed, since we need this template.l