Per [expr.type.conv]:
If the initializer is a parenthesized single expression, the type conversion expression is equivalent to the corresponding cast expression. Otherwise, if the type is cv void and the initializer is () or {} (after pack expansion, if any), the expression is a prvalue of type void that performs no initialization
Meaning T()
when T
is void
(as in your case) is a no-op, and void_t
is defined as (per [temp.alias])
template<typename...> using void_t = void;
Meaning that if a valid type is supplied, then it will always become void
.
Here's my take:
The code is syntactically correct, and if type substitution succeeds it is provably a no-op. Per the as-if rule, the compiler can prove that the program without this line altogether has identical runtime behavior, and therefore it is safe to ignore it completely. I can reproduce this behavior in MSVC, GCC, and Clang.
Inheriter<int> i;
would fail witherror: base specifier must name a class
as you expect godbolt.org/z/516qWc58P – Drainpipevoid_t
? I mean if you use a template that requires an "ok" type as argument you get the expected error, egstd::vector<Inheriter<int>>
. In other words: is the question aboutvoid_t
or aboutInheriter<int>
specifically? You seem to mix two things which could actually make 2 seperate questions – Excludevoid_t
not discarding template parameters which are ill-formed – Kilocalorietemplate <typename T> struct foo {};
as it doesnt actually need to instantiate whatever T is (similar tovoid_t
) – Exclude