5.12.3. 第二条语句 – TEMPLATE_DECL
第二条语句是模板声明,其简要语法树如下。第一条规则是类模板声明,其下是函数模板声明。现在我们正在定义类模板。
template-declaration
├ export [opt] template < template-parameter-list> decl-specific-seq [opt] init-declarator [opt];
Ⅼ export [opt] template < template- parameter -list> function-definition
在 cp_parser_declaration 中,下面的代码片段处理与模板相关的定义。
cp_parser_declaration (continue)
6306 /* If the next token is `template', then we have either a template
6307 declaration, an explicit instantiation, or an explicit
6308 specialization. */
6309 else if (token1.keyword == RID_TEMPLATE)
6310 {
6311 /* `template <>' indicates a template specialization. */
6312 if (token2.type == CPP_LESS
6313 && cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_GREATER)
6314 cp_parser_explicit_specialization (parser);
6315 /* `template <' indicates a template declaration. */
6316 else if (token2.type == CPP_LESS)
6317 cp_parser_template_declaration (parser, /*member_p=*/ false);
6318 /* Anything else must be an explicit instantiation. */
6319 else
6320 cp_parser_explicit_instantiation (parser);
6321 }
6322 /* If the next token is `export', then we have a template
6323 declaration. */
6324 else if (token1.keyword == RID_EXPORT)
6325 cp_parser_template_declaration (parser, /*member_p=*/ false);
6326 /* If the next token is `extern', 'static' or 'inline' and the one
6327 after that is `template', we have a GNU extended explicit
6328 instantiation directive. */
6329 else if (cp_parser_allow_gnu_extensions_p (parser)
6330 && (token1.keyword == RID_EXTERN
6331 || token1.keyword == RID_STATIC
6332 || token1.keyword == RID_INLINE)
6333 && token2.keyword == RID_TEMPLATE)
6334 cp_parser_explicit_instantiation (parser);
当看到开头的符号:“ template <class Host> class SingleThreaded ”,在 6316 行,解析器就能知道这是一个模板声明,并调用处理句柄。
7593 static void
7594 cp_parser_template_declaration (cp_parser* parser, bool member_p) in parser.c
7595 {
7596 /* Check for `export'. */
7597 if (cp_lexer_next_token_is_keyword (parser->lexer, RID_EXPORT))
7598 {
7599 /* Consume the `export' token. */
7600 cp_lexer_consume_token (parser->lexer);
7601 /* Warn that we do not support `export'. */
7602 warning ("keyword `export' not implemented, and will be ignored");
7603 }
7604
7605 cp_parser_template_declaration_after_export (parser, member_p);
7606 }
关键字 export 用于分离编译模式( separation compilation model )。通过分离编译模式,类模板定义及其内联成员函数的定义放在头文件里,而非内联成员函数的定义及静态数据成员放在源文件中。通过这个模式,类模板及其成员的定义,与我们组织非模板类及其成员的方式一致。但是当前版本,尚未支持。
14420 static void
14421 cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p) in parser.c
14422 {
14423 tree decl = NULL_TREE;
14424 tree parameter_list;
14425 bool friend_p = false;
14426
14427 /* Look for the `template' keyword. */
14428 if (!cp_parser_require_keyword (parser, RID_TEMPLATE, "`template'"))
14429 return ;
14430
14431 /* And the `<'. */
14432 if (!cp_parser_require (parser, CPP_LESS, "`<'"))
14433 return ;
14434
14435 /* If the next token is `>', then we have an invalid
14436 specialization. Rather than complain about an invalid template
14437 parameter, issue an error message here. */
14438 if (cp_lexer_next_token_is (parser->lexer, CPP_GREATER))
14439 {
14440 cp_parser_error (parser, "invalid explicit specialization");
14441 begin_specialization ();
14442 parameter_list = NULL_TREE;
14443 }
14444 else
14445 {
14446 /* Parse the template parameters. */
14447 begin_template_parm_list ();
14448 parameter_list = cp_parser_template_parameter_list (parser);
14449 parameter_list = end_template_parm_list (parameter_list);
14450 }
我们知道,在模板参数列表中,形如 class T 的声明不是一个普通的类声明,而是更接近于一个占位符,然后在具现( template instantiation )期间,它将为具体的类型所替代。把这个类型声明从当前绑定域中分离出去,放在特定的域中(即, sk_template_parms ),是很重要的一步。
598 void
599 begin_template_parm_list (void) in pt.c
600 {
601 /* We use a non-tag-transparent scope here, which causes pushtag to
602 put tags in this scope, rather than in the enclosing class or
603 namespace scope. This is the right thing, since we want
604 TEMPLATE_DECLS, and not TYPE_DECLS for template classes. For a
605 global template class, push_template_decl handles putting the
606 TEMPLATE_DECL into top-level scope. For a nested template class,
607 e.g.:
608
609 template <class T> struct S1 {
610 template <class T> struct S2 {};
611 };
612
613 pushtag contains special code to call pushdecl_with_scope on the
614 TEMPLATE_DECL for S2. */
615 begin_scope (sk_template_parms, NULL);
616 ++processing_template_decl ;
617 ++processing_template_parmlist ;
618 note_template_header (0);
619 }
上面的 processing_template_decl 指向 scope_chain 的 x_processing_template_decl 域。它与全局变量 processing_template_parmlist 一起记录了正在处理的模板声明及模板参数列表的嵌套深度。
687 static void
688 note_template_header (int specialization) in pt.c
689 {
690 processing_specialization = specialization;
691 template_header_count ++;
692 }
在加入特殊的域 sk_template_parms 后的布局大致如下。
图 47 :加入 sk_template_parm 后的简要布局
5.12.3.1. 解析模板参数列表
这里 processing_specialization 指向 scope_chain 的 x_processing_specialization 域。如果它不为 0 ,表示找到了特化的模板(即看到 template <> )。而全局变量 template_header_count 如果不为 0 ,表示看到模板头(即, template )。
从下面的简要语法树,这部分的语法相当的复杂。
template < template-parameter-list >
├ template-parameter
Ⅼ template-parameter-list , template-parameter
├ type-parameter
| ├ class identifier [opt]
| ├ class identifier [opt] = type-id
| ├ typename identifier [opt]
| ├ template < template-parameter-list > class identifer [opt]
| Ⅼ template < template-parameter-list > class identifer [opt] = id-expression
|
Ⅼ parameter-declaration
├ decl-specifier-seq declarator
├ decl-specifier-seq declarator = assignment-expression
├ decl-specifier-seq abstract-declarator [opt]
Ⅼ decl-specifier-seq abstract-declarator [opt] = assignment-expression
因为参数列表可以包含任意数目的参数(至少理论上如此),在下面 7622 行的 WHILE 循环由符号“逗号”来驱动。
7617 static tree
7618 cp_parser_template_parameter_list (cp_parser* parser) in parser.c
7619 {
7620 tree parameter_list = NULL_TREE;
7621
7622 while (true)
7623 {
7624 tree parameter;
7625 cp_token *token;
7626
7627 /* Parse the template-parameter. */
7628 parameter = cp_parser_template_parameter (parser);
7629 /* Add it to the list. */
7630 parameter_list = process_template_parm (parameter_list,
7631 parameter);
7632
7633 /* Peek at the next token. */
7634 token = cp_lexer_peek_token (parser->lexer);
7635 /* If it's not a `,', we're done. */
7636 if (token->type != CPP_COMMA)
7637 break ;
7638 /* Otherwise, consume the `,' token. */
7639 cp_lexer_consume_token (parser->lexer);
7640 }
7641
7642 return parameter_list;
7643 }
在上面的语法树中 , template-parameter 的 type-parameter 构建类型参数 , 而 template-parameter 的 parameter-declaration 构建非类型参数。它们的构成极为不同 , 不过它们仍可能拥有相同的头部 , 例如 :
template < typename T, typename T::X X> ,第一个 T 是类型参数,而第二个 T::X 是非类型参数。
同样, template <class C, class D*> , C 是类型参数,而 D* 是非类型参数。
相关的函数要小心处理这些情况。