All standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS.
The following snippet successfully compiles for all standard versions(1) for both Clang and GCC.
template<typename Tag>
struct Tagged {};
Tagged<struct Tag1> t1;
Tagged<struct Tag2> t2;
[temp.arg.type]/1 requires that
A template-argument for a template-parameter which is a type shall be a type-id.
and [temp.arg.type]/2 contains the note
[ Note: A template type argument may be an incomplete type ([basic.types]). — end note ]
Thus, I would visit the grammar for a template-argument as well as a type-id to understand whether the former may also allow declaring an (incomplete) class type; in other words, if a class-head may be used as part of a template-argument; from [class]/1:
class-head: class-key attribute-specifier-seq_opt class-head-name class-virt-specifier_opt base-clause_opt class-key attribute-specifier-seq_opt base-clause_opt ... class-key: class struct union
However, going down the rabbit hole for the grammar of a template-argument, a type-id, an id-expression and eventually an identifier does not lead to, afaict, a grammar accepting a class-head.
I may be looking for the wrong grammar (class-head) to explain whether the snippet above is actually legal or not.
(1) Note that if we place Tagged<struct Tag1> t1;
e.g. within the scope of a function, the code is ill-formed in C++03, as local types were not allowed, back then, as template arguments for type template parameters.
(It seems the OP (myself) did not initially go deep enough down the type-id grammar rabbit hole)
Is it legal to declare a type as part of the template-argument for a type template-parameter?
Yes, this is legal.
If so, which part of the standard (say, C++17) governs this?
A class-key and identifier grammar sequence, say struct Tag1
, is a valid elaborated-type-specifier,
elaborated-type-specifier: class-key attribute-specifier-seq_opt nested-name-specifier_opt identifier ...
which in turn is a valid type-specifier,
type-specifier: elaborated-type-specifier ...
which is turn is a valid single entry of a type-specifier-seq,
type-specifier-seq: type-specifier attribute-specifier-seq_opt
which in turn is a valid type-id,
type-id: type-specifier-seq abstract-declarator_opt ...
and as was noted in the OP, a type-id, as per [temp.arg.type]/1, is a valid template-argument for a type template-parameter;
A template-argument for a template-parameter which is a type shall be a type-id.
Thus, the snippet
template<typename Tag> struct Tagged {}; Tagged<struct Tag1> t1; Tagged<struct Tag2> t2;
is well-formed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With