将类型声明为类型模板参数的模板参数的一部分是否合法?

Is it legal to declare a type as part of the template-argument for a type template-parameter?

以下所有标准参考均指N4659: March 2017 post-Kona working draft/C++17 DIS


以下代码片段成功编译了所有标准版本(1),适用于 Clang 和 GCC。

template<typename Tag>
struct Tagged {};

Tagged<struct Tag1> t1;
Tagged<struct Tag2> t2;

[temp.arg.type]/1 要求

A template-argument for a template-parameter which is a type shall be a type-id.

[temp.arg.type]/2包含注释

[ Note: A template type argument may be an incomplete type ([basic.types]).  — end note ]

因此,我将访问 template-argumenttype-id 的语法,以了解前者是否可能还允许声明一个(不完整的)class 类型;换句话说,如果 class-head 可以用作 template-argument 的一部分;来自 [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

然而,对于 a template-argument, a type-id, an id-expression and eventually an identifier 的语法,深入探索并不会导致接受 class-head 的语法。

我可能在寻找错误的语法 (class-head) 来解释上面的片段是否真的合法。

问题


(1) 请注意,如果我们放置 Tagged<struct Tag1> t1; 例如在函数范围内,代码在 C++03 中格式错误,因为 本地类型 在当时不允许作为类型模板参数的模板参数。

(看来 OP(我自己)最初并没有深入到 type-id 语法兔子洞)


Is it legal to declare a type as part of the template-argument for a type template-parameter?

是的,这是合法的。

If so, which part of the standard (say, C++17) governs this?

一个class-keyidentifier文法序列,比如说struct Tag1,就是a valid elaborated-type-specifier,

elaborated-type-specifier:
    class-key attribute-specifier-seq_opt nested-name-specifier_opt identifier
    ...

这又是 a valid type-specifier,

type-specifier:
    elaborated-type-specifier
    ...

这是 a type-specifier-seq

的有效单个条目
type-specifier-seq:
    type-specifier attribute-specifier-seq_opt

这又是 a valid type-id,

type-id:
    type-specifier-seq abstract-declarator_opt
    ...

并且如 OP 中所述,type-id,根据 [temp.arg.type]/1,是有效的 template-argument 对于类型 模板参数 ;

A template-argument for a template-parameter which is a type shall be a type-id.

因此,片段

template<typename Tag>
struct Tagged {};

Tagged<struct Tag1> t1;
Tagged<struct Tag2> t2;

格式正确。