Reputation: 66210
From the accepted answer of a previous question I've discovered a rule I didn't know about templates and well formedness
The program is ill-formed, no diagnostic required, if:
- [...]
- every valid specialization of a variadic template requires an empty template parameter pack, or
- [...]
According this rule (if I understand correctly), the following template function is ill-formed
template <typename ... Ts>
int foo (std::tuple<Ts...> const &)
{ return std::get<sizeof...(Ts)>(std::tuple<int>{42}); }
because the only valid specialization require and empty Ts...
parameter pack.
But (maybe because I don't know English very well) I'm not sure to understand this rule in case of a template with two ore more parameter packs.
I mean... the following foo()
function
#include <tuple>
#include <iostream>
template <typename ... Ts, typename ... Us>
int foo (std::tuple<Ts...> const &, std::tuple<Us...> const &)
{ return std::get<sizeof...(Ts)+sizeof...(Us)-1U>(std::tuple<int>{42}); }
int main ()
{
auto t0 = std::tuple<>{};
auto t1 = std::tuple<int>{0};
//std::cout << foo(t0, t0) << std::endl; // compilation error
std::cout << foo(t0, t1) << std::endl; // print 42
std::cout << foo(t1, t0) << std::endl; // print 42
//std::cout << foo(t1, t1) << std::endl; // compilation error
}
is well-formed or ill-formed?
Because a valid specialization of it require that Ts...
or Us...
is empty (and that the other parameter pack is exactly of size 1).
The rule should be interpreted in the sense that a programm is ill-formed if there is an empty parameter pack that must be ever empty (so my example should be well formed because both parameter packs can be not empty) or in the sense that is ill-formed if in every specialization there is at least an empty parameter pack, not necessarily the same in every specialization (so my example should be ill-formed) ?
Upvotes: 5
Views: 137
Reputation: 119239
I think the intent of the rule has not been communicated clearly by the standard wording and perhaps never will.
The "no valid specialization" rule is intended to allow compilers to perform early diagnosis of constructs that are grammatically valid but can be seen to be semantically nonsensical without having to instantiate the template. Here's an example:
template <int x>
void foo() {
typedef double D;
x.~D();
}
There's nothing grammatically wrong with this. Semantically, this is trying to destroy a value of type int
as if it were of type double
, which is of course not allowed. If you instantiate foo
then the compiler is allowed to diagnose it, but isn't required to. It seems that Clang gives a diagnostic, while GCC doesn't.
In the same vein, the intent of the rule about template parameter packs is that if the pattern for a pack expansion is semantically nonsensical, then the compiler is allowed to diagnose it early. A variant of the above example illustrates this:
template <int... x>
void foo() {
typedef double D;
(x.~D(), ...);
}
The only way this can be well-formed is if there are 0 instances of x.~D()
in the expansion; even 1 will always be ill-formed. So the idea here is that the compiler is allowed (but not required) to diagnose the ill-formed pattern even though it's possible to get a well-formed specialization by supplying an empty pack that results in 0 elements in the expansion.
(Again, GCC is ok with this, and even lets you call foo<>()
. Clang diagnoses it even when it's not instantiated.)
Based on that, the rule when there are two parameter packs should be:
sizeof...
, then the rule about every valid specialization requiring an empty pack shouldn't even apply at all. It's not in the "spirit" of the current rule.Upvotes: 1