5.12.4.2.2.2.2. 解析实参列表
然后调用 cp_parser_enclosed_template_argument_list 来解析 template-id 中所包含的模板实参列表。在这个处理之前, parser 的 greater_than_is_operator_p 域将被设置为 false ,因为从现在起进来的“ > ”将不被视为操作符。而在实参列表中输入的“ >> ”将被解析作“ > ”和“ > ”,例如,“ A<B<>> a ”,前端将把它视为“ A<B<> > a ”。看到解析器将尝试从这个错误恢复并继续前行,但不保证可以产生正确的代码,因为程序员在这里犯了错误,因此给出错误信息。
14692 static tree
14693 cp_parser_enclosed_template_argument_list (cp_parser* parser) in parser.c
14694 {
14695 tree arguments;
14696 tree saved_scope;
14697 tree saved_qualifying_scope;
14698 tree saved_object_scope;
14699 bool saved_greater_than_is_operator_p;
14700
14701 /* [temp.names]
14702
14703 When parsing a template-id, the first non-nested `>' is taken as
14704 the end of the template-argument-list rather than a greater-than
14705 operator. */
14706 saved_greater_than_is_operator_p
14707 = parser->greater_than_is_operator_p;
14708 parser->greater_than_is_operator_p = false;
14709 /* Parsing the argument list may modify SCOPE, so we save it
14710 here. */
14711 saved_scope = parser->scope;
14712 saved_qualifying_scope = parser->qualifying_scope;
14713 saved_object_scope = parser->object_scope;
14714 /* Parse the template-argument-list itself. */
14715 if (cp_lexer_next_token_is (parser->lexer, CPP_GREATER))
14716 arguments = NULL_TREE;
14717 else
14718 arguments = cp_parser_template_argument_list (parser);
14719 /* Look for the `>' that ends the template-argument-list. If we find
14720 a '>>' instead, it's probably just a typo. */
14721 if (cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT))
14722 {
14723 if (!saved_greater_than_is_operator_p)
14724 {
14725 /* If we're in a nested template argument list, the '>>' has to be
14726 a typo for '> >'. We emit the error message, but we continue
14727 parsing and we push a '>' as next token, so that the argument
14728 list will be parsed correctly.. */
14729 cp_token* token;
14730 error ("`>>' should be `> >' within a nested template argument list");
14731 token = cp_lexer_peek_token (parser->lexer);
14732 token->type = CPP_GREATER;
14733 }
14734 else
14735 {
14736 /* If this is not a nested template argument list, the '>>' is
14737 a typo for '>'. Emit an error message and continue. */
14738 error ("spurious `>>', use `>' to terminate a template argument list");
14739 cp_lexer_consume_token (parser->lexer);
14740 }
14741 }
14742 else
14743 cp_parser_skip_until_found (parser, CPP_GREATER, "`>'");
14744 /* The `>' token might be a greater-than operator again now. */
14745 parser->greater_than_is_operator_p
14746 = saved_greater_than_is_operator_p;
14747 /* Restore the SAVED_SCOPE. */
14748 parser->scope = saved_scope;
14749 parser->qualifying_scope = saved_qualifying_scope;
14750 parser->object_scope = saved_object_scope;
14751
14752 return arguments;
14753 }
parser 的 in_template_argument_list_p 域如果为 true 表示我们正在模板实参列表中。因此我们首先需要缓存这个域的内容,并在解析实参列表之前临时将其设为 true 。
8265 static tree
8266 cp_parser_template_argument_list (cp_parser* parser) in parser.c
8267 {
8268 tree fixed_args[10];
8269 unsigned n_args = 0;
8270 unsigned alloced = 10;
8271 tree *arg_ary = fixed_args;
8272 tree vec;
8273 bool saved_in_template_argument_list_p;
8274
8275 saved_in_template_argument_list_p = parser->in_template_argument_list_p;
8276 parser->in_template_argument_list_p = true;
8277 do
8278 {
8279 tree argument;
8280
8281 if (n_args)
8282 /* Consume the comma. */
8283 cp_lexer_consume_token (parser->lexer);
8284
8285 /* Parse the template-argument. */
8286 argument = cp_parser_template_argument (parser);
8287 if (n_args == alloced)
8288 {
8289 alloced *= 2;
8290
8291 if (arg_ary == fixed_args)
8292 {
8293 arg_ary = xmalloc (sizeof (tree) * alloced);
8294 memcpy (arg_ary, fixed_args, sizeof (tree) * n_args);
8295 }
8296 else
8297 arg_ary = xrealloc (arg_ary, sizeof (tree) * alloced);
8298 }
8299 arg_ary[n_args++] = argument;
8300 }
8301 while (cp_lexer_next_token_is (parser->lexer, CPP_COMMA));
8302
8303 vec = make_tree_vec (n_args);
8304
8305 while (n_args--)
8306 TREE_VEC_ELT (vec, n_args) = arg_ary[n_args];
8307
8308 if (arg_ary != fixed_args)
8309 free (arg_ary);
8310 parser->in_template_argument_list_p = saved_in_template_argument_list_p;
8311 return vec;
8312 }
在列表中的每个参数由 cp_parser_template_argument 解析。当所有的实参都被处理后,相应的所产生的节点被填充入 TREE_VEC 节点,并且该节点被作为返回值返回。
5.12.4.2.2.2.2.1. 解析实参
模板实参的语法树显示如下。
( 点此打开 )
根据【 8 】,在一个模板实参中,由 type-id 及 id-expression 所导致的二义性,按 type-id 来解析,而不考虑对应模板实参的形式(缺省模板实参不存在这样的二义性,因为 template-parameter 的形式确定了缺省 template-argument 必须是 type-id )。
例如:
template <class T> void f();
template <int I> void f();
void g() {
f<int()>(); // int() is a type-id: call the first f()
}
因此我们首先尝试 type-id 。
8330 static tree
8331 cp_parser_template_argument (cp_parser* parser) in parser.c
8332 {
8333 tree argument;
8334 bool template_p;
8335 bool address_p;
8336 bool maybe_type_id = false;
8337 cp_token *token;
8338 cp_id_kind idk;
8339 tree qualifying_class;
8340
8341 /* There's really no way to know what we're looking at, so we just
8342 try each alternative in order.
8343
8344 [temp.arg]
8345
8346 In a template-argument, an ambiguity between a type-id and an
8347 expression is resolved to a type-id, regardless of the form of
8348 the corresponding template-parameter.
8349
8350 Therefore, we try a type-id first. */
8351 cp_parser_parse_tentatively (parser);
8352 argument = cp_parser_type_id (parser);
8353 /* If there was no error parsing the type-id but the next token is a '>>',
8354 we probably found a typo for '> >'. But there are type-id which are
8355 also valid expressions. For instance:
8356
8357 struct X { int operator >> (int); };
8358 template <int V> struct Foo {};
8359 Foo<X () >> 5> r;
8360
8361 Here 'X()' is a valid type-id of a function type, but the user just
8362 wanted to write the expression "X() >> 5". Thus, we remember that we
8363 found a valid type-id, but we still try to parse the argument as an
8364 expression to see what happens. */
8365 if (!cp_parser_error_occurred (parser)
8366 && cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT))
8367 {
8368 maybe_type_id = true;
8369 cp_parser_abort_tentative_parse (parser);
8370 }
8371 else
8372 {
8373 /* If the next token isn't a `,' or a `>', then this argument wasn't
8374 really finished. This means that the argument is not a valid
8375 type-id. */
8376 if (!cp_parser_next_token_ends_template_argument_p (parser))
8377 cp_parser_error (parser, "expected template-argument");
8378 /* If that worked, we're done. */
8379 if (cp_parser_parse_definitely (parser))
8380 return argument;
8381 }
这里有一个棘手的情形需要小心处理。考虑在上面注释中给出的例子(这个例子不能通过编译,但不是因为 >> ):
struct X { int operator >> (int); };
template <int V> struct Foo {};
Foo<X() >> 5> r;
“ X() ”是一个 type-id 的实例,但“ X() >> 5 ”则是移位表达式的实例(这是 assignment-expression 的一个变形,其中“ X() ”是一个 postfix-expression )。因为解析器要尽可能多地在一次中解析符号,它将把该字符串解析为移位表达式。然而由于失误,程序员也可能将“ > > ”写作“ >> ”,而把 type-id 从解析器的角度伸展为移位表达式。因此当成功解析一个后跟“ >> ”的 type-id 时,需要记住这个可能性,并且接着尝试按移位表达式来解析(此时,模板参数是非类型的)。细节在下面 assignment-expression 的情况一节。
5.12.4.2.2.2.2.1.1. type-id 的情形
从其语法树,可以看到 type-id 包含了 type-specifier-seq 及可选的 abstract- declarator 。在经过解析之后,这些部分被包含在一个 TREE_LIST 节点,在其中, type-specifier-seq 在 purpose 域,而 abstract-declarator 在 value 域中。
10924 static tree
10925 cp_parser_type_id (cp_parser* parser) in parser.c
10926 {
10927 tree type_specifier_seq;
10928 tree abstract_declarator;
10929
10930 /* Parse the type-specifier-seq. */
10931 type_specifier_seq
10932 = cp_parser_type_specifier_seq (parser);
10933 if (type_specifier_seq == error_mark_node)
10934 return error_mark_node;
10935
10936 /* There might or might not be an abstract declarator. */
10937 cp_parser_parse_tentatively (parser);
10938 /* Look for the declarator. */
10939 abstract_declarator
10940 = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_ABSTRACT, NULL,
10941 /*parenthesized_p=*/ NULL,
10942 /*member_p=*/ false);
10943 /* Check to see if there really was a declarator. */
10944 if (!cp_parser_parse_definitely (parser))
10945 abstract_declarator = NULL_TREE;
10946
10947 return groktypename (build_tree_list (type_specifier_seq,
10948 abstract_declarator));
10949 }
type-specifier-seq 的语法给出如下。
type-specifier-seq
├ type-specifier type-specifier-seq [opt]
GUN Ext Ⅼ attributes type-specifier-seq
10964 static tree
10965 cp_parser_type_specifier_seq (cp_parser* parser) in parser.c
10966 {
10967 bool seen_type_specifier = false;
10968 tree type_specifier_seq = NULL_TREE;
10969
10970 /* Parse the type-specifiers and attributes. */
10971 while (true)
10972 {
10973 tree type_specifier;
10974
10975 /* Check for attributes first. */
10976 if (cp_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
10977 {
10978 type_specifier_seq = tree_cons (cp_parser_attributes_opt (parser),
10979 NULL_TREE,
10980 type_specifier_seq);
10981 continue ;
10982 }
10983
10984 /* After the first type-specifier, others are optional. */
10985 if (seen_type_specifier)
10986 cp_parser_parse_tentatively (parser);
10987 /* Look for the type-specifier. */
10988 type_specifier = cp_parser_type_specifier (parser,
10989 CP_PARSER_FLAGS_NONE,
10990 /*is_friend=*/ false,
10991 /*is_declaration=*/ false,
10992 NULL,
10993 NULL);
10994 /* If the first type-specifier could not be found, this is not a
10995 type-specifier-seq at all. */
10996 if (!seen_type_specifier && type_specifier == error_mark_node)
10997 return error_mark_node;
10998 /* If subsequent type-specifiers could not be found, the
10999 type-specifier-seq is complete. */
11000 else if (seen_type_specifier && !cp_parser_parse_definitely (parser))
11001 break ;
11002
11003 /* Add the new type-specifier to the list. */
11004 type_specifier_seq
11005 = tree_cons (NULL_TREE, type_specifier, type_specifier_seq);
11006 seen_type_specifier = true;
11007 }
11008
11009 /* We built up the list in reverse order. */
11010 return nreverse (type_specifier_seq);
11011 }
函数 cp_parser_declarator 在这里被递归调用,不过仅是为了 abstract-declarator 。因为 type-id 在语义上是一个对象或函数的声明,其类型忽略对象或函数的名字,这个声明是一个类型的名字。根据解析的结果,应该由 groktypename 构建 *_TYPE 节点。
3640 tree
3641 groktypename (tree typename) in decl.c
3642 {
3643 tree specs, attrs;
3644 tree type;
3645 if (TREE_CODE (typename) != TREE_LIST)
3646 return typename;
3647 split_specs_attrs (TREE_PURPOSE (typename), &specs, &attrs);
3648 type = grokdeclarator (TREE_VALUE (typename), specs,
3649 TYPENAME, 0, &attrs);
3650 if (attrs)
3651 cplus_decl_attributes (&type, attrs, 0);
3652 return type;
3653 }
上面,属性及 type-specifier 被串接入同一个链表,需要把这个链表分成 2 个链表。下面的参数 declspecs 将持有 type-specifier-seq 部分,而 prefix_attributes 将保存属性链表如果出现的话。在 340 行的条件找出了一个整型常量,如‘ 5 ’等。而如果 spec_attrs 不是 tree_list ,则意味着没有属性。
334 void
335 split_specs_attrs (tree specs_attrs, tree *declspecs, tree *prefix_attributes) in attribs.c
336 {
337 tree t, s, a, next, specs, attrs;
338
339 /* This can happen after an __extension__ in pedantic mode. */
340 if (specs_attrs != NULL_TREE
341 && TREE_CODE (specs_attrs) == INTEGER_CST)
342 {
343 *declspecs = NULL_TREE;
344 *prefix_attributes = NULL_TREE;
345 return ;
346 }
347
348 /* This can happen in c++ (eg: decl: typespec initdecls ';'). */
349 if (specs_attrs != NULL_TREE
350 && TREE_CODE (specs_attrs) != TREE_LIST)
351 {
352 *declspecs = specs_attrs;
353 *prefix_attributes = NULL_TREE;
354 return ;
355 }
356
357 /* Remember to keep the lists in the same order, element-wise. */
358
359 specs = s = NULL_TREE;
360 attrs = a = NULL_TREE;
361 for (t = specs_attrs; t; t = next)
362 {
363 next = TREE_CHAIN (t);
364 /* Declspecs have a non-NULL TREE_VALUE. */
365 if (TREE_VALUE (t) != NULL_TREE)
366 {
367 if (specs == NULL_TREE)
368 specs = s = t;
369 else
370 {
371 TREE_CHAIN (s) = t;
372 s = t;
373 }
374 }
375 /* The TREE_PURPOSE may also be empty in the case of
376 __attribute__(()). */
377 else if (TREE_PURPOSE (t) != NULL_TREE)
378 {
379 if (attrs == NULL_TREE)
380 attrs = a = TREE_PURPOSE (t);
381 else
382 {
383 TREE_CHAIN (a) = TREE_PURPOSE (t);
384 a = TREE_PURPOSE (t);
385 }
386 /* More attrs can be linked here, move A to the end. */
387 while (TREE_CHAIN (a) != NULL_TREE)
388 a = TREE_CHAIN (a);
389 }
390 }
391
392 /* Terminate the lists. */
393 if (s != NULL_TREE)
394 TREE_CHAIN (s) = NULL_TREE;
395 if (a != NULL_TREE)
396 TREE_CHAIN (a) = NULL_TREE;
397
398 /* All done. */
399 *declspecs = specs;
400 *prefix_attributes = attrs;
401 }
对于 type-specifier-seq ,此处,可以看到 type-specifiers 总是通过 tree_list 的 value 域串接起来,而 abstract-declarator 连同其属性总是由 purpose 域来串接。然后函数 grokdeclarator 为该对象或函数的类型返回相应的 TYPE_DECL 节点。在前面的章节中,我们已经看过这个函数的几个例子,因此这里我们跳过它。接下来 type-id 的属性将由 cplus_decl_attributes 安装入这个 TYPE_DECL 节点。
在 4.3.1.7.5.6.5. 处理内建函数的属性 一节中,我们已经看到前端使用数据结构 attribute_spec 来描述每个属性。表 common_attribute_table , attribute_table , format_attribute_table 用于 C++ ,而 format_attribute_table 用于 x86 (目标机器)。它们预定义了可以在语言中及指定平台上使用的所有属性。
1102 void
1103 cplus_decl_attributes (tree *decl, tree attributes, int flags) in decl2.c
1104 {
1105 if (*decl == NULL_TREE || *decl == void_type_node)
1106 return ;
1107
1108 if (TREE_CODE (*decl) == TEMPLATE_DECL)
1109 decl = &DECL_TEMPLATE_RESULT (*decl);
1110
1111 decl_attributes (decl, attributes, flags);
1112
1113 if (TREE_CODE (*decl) == TYPE_DECL)
1114 SET_IDENTIFIER_TYPE_VALUE (DECL_NAME (*decl), TREE_TYPE (*decl));
1115 }
当调用 decl_attributes 时,我们可以通过向参数 flags 设置特定的 enum attribute_flags 的值,来指定要安装的属性,而没有安装的属性将通过一个链表返回。这里我们把 flags 指定为 0 ,表示安装所有提供的属性。
在 4.3.1.7.5.6.5. 处理内建函数的属性 一节中,我们已经看到绝大多数属性具有关联的处理句柄,这些句柄将适当地修改树节点。除此之外,已应用的属性通过宏 *_ATTRIBUTES 链接入树节点的 attributes 域。这些宏同时也作为一个快速的方法来查看在该节点上应用了哪些属性。
5.12.4.2.2.2.2.1.2. id-expression 的情形
如果这不是 type-id ,那么我们按 id-expression 重新解析符号。
cp_parser_template_argument (continue)
8382 /* We're still not sure what the argument will be. */
8383 cp_parser_parse_tentatively (parser);
8384 /* Try a template. */
8385 argument = cp_parser_id_expression (parser,
8386 /*template_keyword_p=*/ false,
8387 /*check_dependency_p=*/ true,
8388 &template_p,
8389 /*declarator_p=*/ false);
8390 /* If the next token isn't a `,' or a `>', then this argument wasn't
8391 really finished. */
8392 if (!cp_parser_next_token_ends_template_argument_p (parser))
8393 cp_parser_error (parser, "expected template-argument");
8394 if (!cp_parser_error_occurred (parser))
8395 {
8396 /* Figure out what is being referred to. If the id-expression
8397 was for a class template specialization, then we will have a
8398 TYPE_DECL at this point. There is no need to do name lookup
8399 at this point in that case. */
8400 if (TREE_CODE (argument) != TYPE_DECL)
8401 argument = cp_parser_lookup_name (parser, argument,
8402 /*is_type=*/ false,
8403 /*is_template=*/ template_p,
8404 /*is_namespace=*/ false,
8405 /*check_dependency=*/ true);
8406 if (TREE_CODE (argument) != TEMPLATE_DECL
8407 && TREE_CODE (argument) != UNBOUND_CLASS_TEMPLATE)
8408 cp_parser_error (parser, "expected template-name");
8409 }
8410 if (cp_parser_parse_definitely (parser))
8411 return argument;
在 5.12.3.2.1.1.3.4.1. 解析声明符 一节中已经给出了一个 cp_parser_id_expression 的例子。如果查找的结果不是 TYPE_DECL 或 CPP_TEMPLATE_ID ,如果没有发生错误,它必须是一个标识符;那么这个名字将进一步被查找以找出关联的声明,就像 8401 行所作的那样。
5.12.4.2.2.2.2.1.3. assignment-expression 的情形
如果我们按 type-id 解析成功,并且看到后面跟着“ >> ”,或者上面的尝试都失败了,该模板实参可能是 assignment-expression 的形式。在这个情形下,这个模板实参必须是非类型实参。根据 [3] ,非类型模板实参可以是:
— 一个具有整型或枚举类型的整形常量表达式;或者
— 一个非类型模板参数的名字;或者
— 表达为 & id-expression ,其中 & 是可选的,如果该名字引用一个函数或数组,具有外部链接性的对象或函数的地址,包括函数模板及函数 template-id ,但不包括的非静态类成员;或者对应的模板参数是一个引用,具有外部链接性的对象或函数;或者
— 指向一个成员的指针
cp_parser_template_argument (continue)
8412 /* It must be a non-type argument. There permitted cases are given
8413 in [temp.arg.nontype]:
8414
8415 -- an integral constant-expression of integral or enumeration
8416 type; or
8417
8418 -- the name of a non-type template-parameter; or
8419
8420 -- the name of an object or function with external linkage...
8421
8422 -- the address of an object or function with external linkage...
8423
8424 -- a pointer to member... */
8425 /* Look for a non-type template parameter. */
8426 if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
8427 {
8428 cp_parser_parse_tentatively (parser);
8429 argument = cp_parser_primary_expression (parser,
8430 &idk,
8431 &qualifying_class);
8432 if (TREE_CODE (argument) != TEMPLATE_PARM_INDEX
8433 || !cp_parser_next_token_ends_template_argument_p (parser))
8434 cp_parser_simulate_error (parser);
8435 if (cp_parser_parse_definitely (parser))
8436 return argument;
8437 }
在按 assignment-expression 解析该模板实参期间,如果我们看到第一个符号是一个标识符,从图形: 语句语法树 ,可以看到它一定是 assignment-expression 语法树中 primary-expression 部分的头部。如果 primary-expression 能被成功解析,我们就做完了。
否则,就检查在 primary-expression 之前是否有引导的 & 。这样形式的实参对应于后 2 种形式的非类型实参(记住该实参必须是编译时常量,对于指向成员的指针,它必须像:“ &A::f ”)。同样如果模板实参可以被解析为 primary-expression ,我们就完成了。我们不进入 primary-expression 的处理细节。在 5.12.4.1.2. 非类型参数 一节中可以找到一些相关的解释。
cp_parser_template_argument (continue)
8438 /* If the next token is "&", the argument must be the address of an
8439 object or function with external linkage. */
8440 address_p = cp_lexer_next_token_is (parser->lexer, CPP_AND);
8441 if (address_p)
8442 cp_lexer_consume_token (parser->lexer);
8443 /* See if we might have an id-expression. */
8444 token = cp_lexer_peek_token (parser->lexer);
8445 if (token->type == CPP_NAME
8446 || token->keyword == RID_OPERATOR
8447 || token->type == CPP_SCOPE
8448 || token->type == CPP_TEMPLATE_ID
8449 || token->type == CPP_NESTED_NAME_SPECIFIER)
8450 {
8451 cp_parser_parse_tentatively (parser);
8452 argument = cp_parser_primary_expression (parser,
8453 &idk,
8454 &qualifying_class);
8455 if (cp_parser_error_occurred (parser)
8456 || !cp_parser_next_token_ends_template_argument_p (parser))
8457 cp_parser_abort_tentative_parse (parser);
8458 else
8459 {
8460 if (qualifying_class)
8461 argument = finish_qualified_id_expr (qualifying_class,
8462 argument,
8463 /*done=*/ true,
8464 address_p);
8465 if (TREE_CODE (argument) == VAR_DECL)
8466 {
8467 /* A variable without external linkage might still be a
8468 valid constant-expression, so no error is issued here
8469 if the external-linkage check fails. */
8470 if (!DECL_EXTERNAL_LINKAGE_P (argument))
8471 cp_parser_simulate_error (parser);
8472 }
8473 else if (is_overloaded_fn (argument))
8474 /* All overloaded functions are allowed; if the external
8475 linkage test does not pass, an error will be issued
8476 later. */
8477 ;
8478 else if (address_p
8479 && (TREE_CODE (argument) == OFFSET_REF
8480 || TREE_CODE (argument) == SCOPE_REF))
8481 /* A pointer-to-member. */
8482 ;
8483 else
8484 cp_parser_simulate_error (parser);
8485
8486 if (cp_parser_parse_definitely (parser))
8487 {
8488 if (address_p)
8489 argument = build_x_unary_op (ADDR_EXPR, argument);
8490 return argument;
8491 }
8492 }
8493 }
8494 /* If the argument started with "&", there are no other valid
8495 alternatives at this point. */
8496 if (address_p)
8497 {
8498 cp_parser_error (parser, "invalid non-type template argument");
8499 return error_mark_node;
8500 }
上面 qualifying_class 由 cp_parser_primary_expression 设置,表示 pointer-to-member 中的限定类。如果设置了 qualifyng_class ,在 8461 行, finish_qualified_id_expr 在 qualifying_class 的上下文查找 argument ,并为形如:“ A::f ”或“ a.*f ”的表达式构建相应的 OFFSET_REF 节点。然后在 8489 行, build_x_unary_op 为形如:“ &A::f ”的表达式构建相应的 ADDR_EXPR 节点。
如果上面的尝试都失败,那么看一下 assignment-expression 的语法树,根据【 3 】的要求,非类型模板实参必须是常量表达式。在这个语法树中,第二及第三规则不是常量表达式;而且常量表达式( constant-expression )可直接降为条件表达式( xxx? xxx: xxx )。
assignment-expression
├ condition-expression
| Ⅼ condition-expression
├ logical-or-expression assignment-operator assignment-expression
Ⅼ throw-expression
因此下一步我们尝试常量表达式。看到下面的 maybe_type_id 如果是 true 表示一开始按 type-id 来解析是成功的,并且其后跟着“ >> ”。因此如果 maybe_type_id 是 true ,意味着我们要尝试把模板实参按移位表达式来解析,它为常量表达式语法所涵盖。
cp_parser_template_argument (continue)
8501 /* If the argument wasn't successfully parsed as a type-id followed
8502 by '>>', the argument can only be a constant expression now.
8503 Otherwise, we try parsing the constant-expression tentatively,
8504 because the argument could really be a type-id. */
8505 if (maybe_type_id)
8506 cp_parser_parse_tentatively (parser);
8507 argument = cp_parser_constant_expression (parser,
8508 /*allow_non_constant_p=*/ false,
8509 /*non_constant_p=*/ NULL);
8510 argument = fold_non_dependent_expr (argument);
8511 if (!maybe_type_id)
8512 return argument;
8513 if (!cp_parser_next_token_ends_template_argument_p (parser))
8514 cp_parser_error (parser, "expected template-argument");
8515 if (cp_parser_parse_definitely (parser))
8516 return argument;
8517 /* We did our best to parse the argument as a non type-id, but that
8518 was the only alternative that matched (albeit with a '>' after
8519 it). We can assume it's just a typo from the user, and a
8520 diagnostic will then be issued. */
8521 return cp_parser_type_id (parser);
8522 }
而如果 maybe_type_id 是 false ,这是我们最后的机会,返回这个结果,不检查是否发生错误(错误消息由处理函数产生)。否则,需要确认该移位表达式正确构成了实参——即在它之后是否跟着“ , ”或“ > ”(注意,如果错把正确的非类型实参给了类型参数,这里是无法查出的,但在后面的处理中会发现实参与参数类型的不匹配)。如果不能,重新按 type-id 来解析符号,因为很可能是误把“ > > ”写作“ >> ”,这样编译器可以给出更合理的错误信息(看回 cp_parser_enclosed_template_argument_list ,它还将处理额外的“ >> ”返回)。