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
CWG3227 [temp.res.general] Unclear and incomplete requirement that packs must possibly be non-empty #878
The program is ill-formed, no diagnostic required, if
...
every valid specialization of a variadic template requires an empty template parameter pack, or
There seem to be several issues with this text:
It makes programs which do static_assert(!sizeof...(pack)) IFNDR, which seems unintended.
If a template does static_assert(false), then it has no valid specializations so vacuously it is IFNDR because of this wording.
It seems to make the following program IFNDR, for no apparent reason:
template<typename...T,typename...U>voidfoo(U...){
int x[!sizeof...(T)!=!sizeof...(U)];//exactly one pack must be empty
}
This does not apply to structured binding packs, even though existing implementations apply the same restrictions:
voidfoo(auto x){
auto[...y]=x;
union U:decltype(y)...{};//unions cannot have base classes, requires y be an empty pack
}
intmain(){
structS{};
foo(S{});
}
It is unclear if this requirement makes zero dimensional mdspans IFNDR because of [mdspan.mdspan.members]/1:
Constraints:
...
sizeof...(OtherIndexTypes) == rank() is true
For the last point, it is unclear whether or not "every valid specialization" of a member function template in a class template refers to just the valid specializations for one member function template in a particular specialization of the class template, or instead collectively to all valid specializations of the member function templates for all specializations of the class template.
changed the title [-][temp.res.general] Unclear and incomplete requirement that packs must possibly be empty[/-][+][temp.res.general] Unclear and incomplete requirement that packs must possibly be non-empty[/+]on Mar 31, 2026
It is unclear if "every valid specialization" includes impossible to spell specializations. In this example there is nothing that would require either pack to be empty, however it is impossible to actually create a specialization that has the second pack containing anything.
This issue and the fifth point also seem applicable to the other bullets, for example:
#include<concepts>template<typename T>structS{
template<typename U>voidfoo()requires std::same_as<T,int>{}//S<float>::foo has no valid specializationstemplate<typename>S(){}//it is impossible to create a specialization of this constructor
};
Perhaps it would make sense to split this into another issue.
The restriction seemingly harms the expressivity of the language.
E.g., some components in the standard library require some type template arguments not to be explicitly specified, or (e.g., algorithms) render the program possibly ill-formed if some template arguments are explicitly specified. Withe valid-on-empty-only parameter packs, we can simply express the requirements as following.
It makes programs which do static_assert(!sizeof...(pack)) IFNDR, which seems unintended.
What makes you believe this is unintended?
(I do agree with applying the "no valid specialization..." phrasing here.)
It would not make sense to allow static_assert(false) but not allow static_assert(!sizeof...(pack)), and copying over the wording about static_assert from the previous bullets would allow both.
changed the title [-][temp.res.general] Unclear and incomplete requirement that packs must possibly be non-empty[/-][+]CWG3227 [temp.res.general] Unclear and incomplete requirement that packs must possibly be non-empty[/+]on Aug 30, 2026
Would this resolution make [dcl.struct.bind] example 1 IFNDR? I think the wording should focus on when the pack expansions happen for example in union U:decltype(x)... rather than attempting to forbid always empty packs entirely.
Well, for "regular" variadic templates, the existing (and the revised) wording prohibits always-empty packs wholesale, even if those are not per se "bad".
For structured bindings, I agree the example seems to convey the intent that an empty structured binding is unconditionally supported. I think we don't need wording beyond [temp.variadic] p7 to handle your example, though.
Activity
[-][temp.res.general] Unclear and incomplete requirement that packs must possibly be empty[/-][+][temp.res.general] Unclear and incomplete requirement that packs must possibly be non-empty[/+]jensmaurer commentedon Mar 31, 2026
Looks like the bullet 1-3 phrasing should be applied to bullet 6 as well.
Halalaluyafail3 commentedon Mar 31, 2026
Another issue I realized is that:
It is unclear if "every valid specialization" includes impossible to spell specializations. In this example there is nothing that would require either pack to be empty, however it is impossible to actually create a specialization that has the second pack containing anything.
This issue and the fifth point also seem applicable to the other bullets, for example:
Perhaps it would make sense to split this into another issue.
Halalaluyafail3 commentedon Apr 11, 2026
Here is another example of when the fifth point would apply:
g<h>is creating a closure type whose call operator is a template that can only be invoked with an empty template parameter pack.Halalaluyafail3 commentedon Aug 28, 2026
Also, something that should be considered is:
This looks to be valid since x can be non-empty, but there is implementation divergence. GCC and MSVC accept this, while Clang and EDG reject it.
frederick-vs-ja commentedon Aug 28, 2026
The restriction seemingly harms the expressivity of the language.
E.g., some components in the standard library require some type template arguments not to be explicitly specified, or (e.g., algorithms) render the program possibly ill-formed if some template arguments are explicitly specified. Withe valid-on-empty-only parameter packs, we can simply express the requirements as following.
This can disallow writing
fun<AnyArg>(x). (fun<>(x)is still allowed, but that should be very fine.)jensmaurer commentedon Aug 28, 2026
What makes you believe this is unintended?
(I do agree with applying the "no valid specialization..." phrasing here.)
Halalaluyafail3 commentedon Aug 28, 2026
It would not make sense to allow
static_assert(false)but not allowstatic_assert(!sizeof...(pack)), and copying over the wording about static_assert from the previous bullets would allow both.You can work around this by creating a helper like the classic always_false that was used with static_assert before:
Or even just
always_false<pack...>||!sizeof...(pack).jensmaurer commentedon Aug 30, 2026
I don't follow.
static_assert(false)is expressly allowed (with a non-dependent argument) because people wanted to writeif constexpr (something) { static_assert(false); }Note that the question here is not "is this useful", but "is this a core issue".
Halalaluyafail3 commentedon Aug 30, 2026
static_assert(std::same_as<T,int>&&std::same_as<T,float>)is also valid, I do not see any requirement that the expression must be non-dependent.jensmaurer commentedon Aug 30, 2026
CWG3227
[-][temp.res.general] Unclear and incomplete requirement that packs must possibly be non-empty[/-][+]CWG3227 [temp.res.general] Unclear and incomplete requirement that packs must possibly be non-empty[/+]Halalaluyafail3 commentedon Aug 30, 2026
Would this resolution make [dcl.struct.bind] example 1 IFNDR? I think the wording should focus on when the pack expansions happen for example in
union U:decltype(x)...rather than attempting to forbid always empty packs entirely.jensmaurer commentedon Aug 31, 2026
Well, for "regular" variadic templates, the existing (and the revised) wording prohibits always-empty packs wholesale, even if those are not per se "bad".
For structured bindings, I agree the example seems to convey the intent that an empty structured binding is unconditionally supported. I think we don't need wording beyond [temp.variadic] p7 to handle your example, though.
CWG3227 is updated.