You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
CWG3179 [dcl.fct] p3 void parameter function and SFINAE #889
Here the function template foo declares a single unnamed non-object parameter of non-dependent type void.
Is this supposed to result in a substitution failure?
Suggested resolution:
Reject the function template declaration as invalid.
Change [dcl.fct] p3 sentence 4:
A parameter list consisting of a single unnamed non-object parameter of non-dependent type equivalent to ([temp.over.link]) void is equivalent to an empty parameter list.
template void foo<int&>(); would be ill-formed due to substitution failure (pointer-to-reference) per [temp.alias] p3.
The current phrasing in [dcl.fct] p3 is the result of CWG2915 and CWG577. To me, the parameter of the "foo" template looks dependent, thus the explicit specialization doesn't find its primary template due to arity mismatch.
template void foo<int&>(); would be ill-formed due to substitution failure (pointer-to-reference) per [temp.alias] p3.
The current phrasing in [dcl.fct] p3 is the result of CWG2915 and CWG577. To me, the parameter of the "foo" template looks dependent, thus the explicit specialization doesn't find its primary template due to arity mismatch.
I think the example in [temp.alias]p3 is mistaken, the template-id is only instantiation-dependent, but it's not dependent.
Looking at [temp.alias] p2, I don't see how substitution of void_t with dependent template arguments would result in a dependent type, since the underlying type is not dependent.
So I think in the current model void_t specializations are always instantiation-dependent but not dependent.
FWIW this touches a bit in possible resolutions for CWG1980.
GCC Implements a resolution where there is a distinction between simple type aliases and non-simple ones.
The rules involve the distinction of template type aliases with unused template parameters, involving some other criteria.
But how GCC resolves this is by making these non-simple spcializations as dependent.
Clang is soon implementing a different model, where these specializations stay only instantiation-dependent, but are nonetheless not equivalent under [temp.link].
[temp.alias] p3 (and its example in particular) explicitly codifies design intent; see CWG1558.
The term "instantiation-dependent" doesn't exist in the C++ standard, and it's probably past due that we introduce it. Until then, I think it's ok to talk about "non-dependent type void" in the rule in [dcl.fct]. (We probably want "not instantiation-dependent" there.)
Other points: [temp.over.link] talks about expressions etc. being equivalent, but is almost silent about types being equivalent. Further, [temp.dep.type] p10.11 says a type is dependent if it is "denoted by a simple-template-id in which [...] any of the template arguments is dependent". Which is exactly the case for the void_t situation. Just because [temp.alias] p2 does some early substitution doesn't imply (to me) that [temp.dep.type] p10.11 (for the original template-id) no longer applies. And thus, the typename is required.
FWIW all the problems that exist for void_t, also have equivalent problems with certain usages of decltype, both in the same spirit, and also ones that involve value-dependent expressions, after CWG2064 was adopted.
I think you could you make the case that this [temp.dep.type] p10.11 wording pre-exists that paper, before the distinction had to be made between class templates and type alias templates, as for example it exists in an earlier draft, N2009.
If you're saying that we miss a formal treatment of "instantiation-dependent" in the standard, I agree. I'm not finding a core issue for that, though. I thought we had one.
Other related core issues for alias templates and "typename" in particular: CWG2236, CWG1390
I believe that addresses your dependent type objections.
With regards to my proposed wording, I still think it's correct, but still leaves things in an unfinished state because we have never established how equivalence rules work for these decltypes.
For that, I agree we need to establish what instantiation-dependence is, and then say something to the effect that:
"Two instantiation-dependent decltype-specifiers are equivalent if their expressions are equivalent".
But IMO we don't need to wait for that to solve the issue, we know we need equivalency here, and we can provide a definition to that later.
Is there any reason we need to instantiate with int& ? Instantiating with plain int would also "work" in the sense that we want this to be ill-formed, because we don't match the arity of the primary template. Instantiating with int& raises "pointer-to-reference" questions that seem to sidetrack, right?
[temp.type] p4 already says almost what you want to say for decltype.
However, I won't introduce a use of the undefined term "instantiation-dependent" with the hopes of providing a definition later. The narrow concern here is a declaration f(U) where U resolves to "void". Can we just say "U is not dependent and no expression appearing in the parameter-declaration is type-dependent or value-dependent"?
Hm... What about
template<class T>
struct S {
using U = decltype((void)(T*)0);
void f(U) {} // zero parameters or any instantiation is ill-formed because forming f(void) ?
};
I notice that this rule is primarily for C compatibility, so maybe we can restrict that rule to non-templated functions, which avoids all "dependent" questions.
changed the title [-][dcl.fct] p3 `void` parameter function and SFINAE[/-][+]CWG3179 [dcl.fct] p3 `void` parameter function and SFINAE[/+]on Apr 22, 2026
Is there any reason we need to instantiate with int& ? Instantiating with plain int would also "work" in the sense that we want this to be ill-formed, because we don't match the arity of the primary template. Instantiating with int& raises "pointer-to-reference" questions that seem to sidetrack, right?
It works in the sense that it's an example of something we would disallow with my proposed resolution.
The example with pointer-to-reference is just to illustrate a possible substitution failure, and why this matters.
[temp.type] p4 already says almost what you want to say for decltype.
Yeah, but it's talking about same type, which is not the same (ha!) as equivalent type.
I think we will need this distinction.
So decltype((void)(T*)0) is the same type as void, but is not equivalent to void.
However, I won't introduce a use of the undefined term "instantiation-dependent" with the hopes of providing a definition later. The narrow concern here is a declaration f(U) where U resolves to "void". Can we just say "U is not dependent and no expression appearing in the parameter-declaration is type-dependent or value-dependent"?
Hm... What about
template<class T>
struct S {
using U = decltype((void)(T*)0);
void f(U) {} // zero parameters or any instantiation is ill-formed because forming f(void) ?
};
The latter, this doesn't matter because any instantiation is ill-formed.
I think any definition of instantiation-dependence needs to propagate syntactically instead of semantically, ie a type becomes transitively instantiation-dependent if any instantiation-dependent types or expressions appear syntactically within it.
So even if the underlying type of the alias U is instantiation-dependent, the type U itself is not instantiation-dependent.
I notice that this rule is primarily for C compatibility, so maybe we can restrict that rule to non-templated functions, which avoids all "dependent" questions.
Activity
jensmaurer commentedon Apr 21, 2026
template void foo<int&>();would be ill-formed due to substitution failure (pointer-to-reference) per [temp.alias] p3.The current phrasing in [dcl.fct] p3 is the result of CWG2915 and CWG577. To me, the parameter of the "foo" template looks dependent, thus the explicit specialization doesn't find its primary template due to arity mismatch.
mizvekov commentedon Apr 21, 2026
I think the example in [temp.alias]p3 is mistaken, the template-id is only instantiation-dependent, but it's not dependent.
Looking at [temp.alias] p2, I don't see how substitution of
void_twith dependent template arguments would result in a dependent type, since the underlying type is not dependent.So I think in the current model
void_tspecializations are always instantiation-dependent but not dependent.FWIW this touches a bit in possible resolutions for CWG1980.
GCC Implements a resolution where there is a distinction between
simpletype aliases and non-simple ones.The rules involve the distinction of template type aliases with unused template parameters, involving some other criteria.
But how GCC resolves this is by making these non-simple spcializations as dependent.
Clang is soon implementing a different model, where these specializations stay only instantiation-dependent, but are nonetheless not equivalent under [temp.link].
The difference can be observed in this example:
Is the
typenamerequired above? Per GCC's resolution, yes.Per Clang's, the
typenameis still optional.jensmaurer commentedon Apr 21, 2026
[temp.alias] p3 (and its example in particular) explicitly codifies design intent; see CWG1558.
The term "instantiation-dependent" doesn't exist in the C++ standard, and it's probably past due that we introduce it. Until then, I think it's ok to talk about "non-dependent type void" in the rule in [dcl.fct]. (We probably want "not instantiation-dependent" there.)
Other points: [temp.over.link] talks about expressions etc. being equivalent, but is almost silent about types being equivalent. Further, [temp.dep.type] p10.11 says a type is dependent if it is "denoted by a simple-template-id in which [...] any of the template arguments is dependent". Which is exactly the case for the void_t situation. Just because [temp.alias] p2 does some early substitution doesn't imply (to me) that [temp.dep.type] p10.11 (for the original template-id) no longer applies. And thus, the
typenameis required.mizvekov commentedon Apr 21, 2026
FWIW all the problems that exist for
void_t, also have equivalent problems with certain usages ofdecltype, both in the same spirit, and also ones that involve value-dependent expressions, after CWG2064 was adopted.So if you replace the initial example with:
According to https://eel.is/c++draft/temp.type#4, that decltype is not dependent.
mizvekov commentedon Apr 21, 2026
Err, this is a better example:
Another example:
Or, without void_t:
mizvekov commentedon Apr 21, 2026
Also, https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf never touched the wording for [temp.dep.type].
I think you could you make the case that this [temp.dep.type] p10.11 wording pre-exists that paper, before the distinction had to be made between class templates and type alias templates, as for example it exists in an earlier draft, N2009.
jensmaurer commentedon Apr 22, 2026
If you're saying that we miss a formal treatment of "instantiation-dependent" in the standard, I agree. I'm not finding a core issue for that, though. I thought we had one.
Other related core issues for alias templates and "typename" in particular: CWG2236, CWG1390
mizvekov commentedon Apr 22, 2026
I still think we can solve this issue (OP) at hand independently of the other concerns.
So stepping back a little, if we go back to my original post, and instead replace my example with:
I believe that addresses your dependent type objections.
With regards to my proposed wording, I still think it's correct, but still leaves things in an unfinished state because we have never established how equivalence rules work for these decltypes.
For that, I agree we need to establish what instantiation-dependence is, and then say something to the effect that:
"Two instantiation-dependent decltype-specifiers are equivalent if their expressions are equivalent".
But IMO we don't need to wait for that to solve the issue, we know we need equivalency here, and we can provide a definition to that later.
jensmaurer commentedon Apr 22, 2026
Is there any reason we need to instantiate with
int&? Instantiating with plainintwould also "work" in the sense that we want this to be ill-formed, because we don't match the arity of the primary template. Instantiating withint&raises "pointer-to-reference" questions that seem to sidetrack, right?[temp.type] p4 already says almost what you want to say for decltype.
However, I won't introduce a use of the undefined term "instantiation-dependent" with the hopes of providing a definition later. The narrow concern here is a declaration
f(U)whereUresolves to "void". Can we just say "U is not dependent and no expression appearing in the parameter-declaration is type-dependent or value-dependent"?Hm... What about
I notice that this rule is primarily for C compatibility, so maybe we can restrict that rule to non-templated functions, which avoids all "dependent" questions.
[-][dcl.fct] p3 `void` parameter function and SFINAE[/-][+]CWG3179 [dcl.fct] p3 `void` parameter function and SFINAE[/+]jensmaurer commentedon Apr 22, 2026
CWG3179
mizvekov commentedon Apr 22, 2026
It works in the sense that it's an example of something we would disallow with my proposed resolution.
The example with pointer-to-reference is just to illustrate a possible substitution failure, and why this matters.
Yeah, but it's talking about same type, which is not the same (ha!) as equivalent type.
I think we will need this distinction.
So
decltype((void)(T*)0)is the same type asvoid, but is not equivalent tovoid.The latter, this doesn't matter because any instantiation is ill-formed.
I think any definition of instantiation-dependence needs to propagate syntactically instead of semantically, ie a type becomes transitively instantiation-dependent if any instantiation-dependent types or expressions appear syntactically within it.
So even if the underlying type of the alias
Uis instantiation-dependent, the typeUitself is not instantiation-dependent.That also works.
mizvekov commentedon Apr 29, 2026
According to https://eel.is/c++draft/expr.prim.req#general-4, this is valid:
The current proposed resolution would disallow that, and this is fine for me.
Otherwise, all the same problems would apply there.
mizvekov commentedon Apr 29, 2026
Also, what about function types in general?
I think this proposed resolution would disallow void parameter type on all function types, wouldn't it?