Consider the following code:
template<typename F>
struct S;
template<typename Ret, typename... Args>
struct S<Ret(Args...)> { };
template<typename... Args>
using Alias = S<void(Args...)>;
int main() {
S<void(int)> s;
Alias<int> alias;
}
It works fine, as expected and both the line involving S
and the one involving Alias
define under the hood the same type S<void(int)>
.
Now, consider the following changes:
int main() {
S<void(void)> s; // this line compiles
Alias<void> alias; // this line does not
}
I expected it to compile, for reasons that are similar to the ones above mentioned.
It goes without saying that it doesn't compile because of the line involving Alias
, instead I get the error:
In substitution of 'template using Alias = S [with Args = {void}]'
[...]
error: invalid parameter type 'void'
The question is pretty simple: what I missed here?
From [dcl.fct], emphasis mine:
A parameter list consisting of a single unnamed parameter of non-dependent type
void
is equivalent to an empty parameter list. Except for this special case, a parameter shall not have type cvvoid
.
In this case, Args...
is a dependent type pack, so void
is not allowed there. This idea is repeated in a note in [temp.deduct]:
[ Note: Type deduction may fail for the following reasons:
— [...]
— Attempting to create a function type in which a parameter has a type ofvoid
, or in which the return type is a function type or array type.
— [...]
—end note ]
Note that S<void(void)>
compiles since void(void)
is non-dependent and is equivalent to void()
, so Ret(Args...)
is never deduced to have void
in the parameter list - it's deduced with Args...
empty.
At least there's a simple workaround in that you can just write Alias<>
.