函数体构成了一个新的块绑定域,在更深入进入该函数体前,要执行之前挂起的访问检查,以尽早发现违例的访问。
cp_parser_function_definition_from_specifiers_and_declarator (continue)
14329 /* If there were names looked up in the decl-specifier-seq that we
14330 did not check, check them now. We must wait until we are in the
14331 scope of the function to perform the checks, since the function
14332 might be a friend. */
14333 perform_deferred_access_checks ();
14334
14335 if (!success_p)
14336 {
14337 /* If begin_function_definition didn't like the definition, skip
14338 the entire function. */
14339 error ("invalid function declaration");
14340 cp_parser_skip_to_end_of_block_or_statement (parser);
14341 fn = error_mark_node;
14342 }
14343 else
14344 fn = cp_parser_function_definition_after_declarator (parser,
14345 /*inline_p=*/ false);
14346
14347 return fn;
14348 }
在 5.12.3.2.1.2.1.2.解析函数体 一节看过 cp_parser_function_definition_after_declarator 。在这个函数中, cp_parser_ctor_initializer_opt_and_function_body 解析函数体,以及对于构造函数可能存在的初始化值。另外在这个函数中, begin_function_body 准备了复合语句的子树的根节点,以及其封闭作用域。之后,相关的中间树部分如下。
( 点此打开 )
而 cp_parser_function_body 处理作为一个复合语句的函数体。
11460 static void
11461 cp_parser_function_body (cp_parser *parser) in parser.c
11462 {
11463 cp_parser_compound_statement (parser, false);
11464 }
5651 static tree
5652 cp_parser_compound_statement (cp_parser *parser, bool in_statement_expr_p)
5653 {
5654 tree compound_stmt;
5655
5656 /* Consume the `{'. */
5657 if (!cp_parser_require (parser, CPP_OPEN_BRACE, "`{'"))
5658 return error_mark_node;
5659 /* Begin the compound-statement. */
5660 compound_stmt = begin_compound_stmt (/*has_no_scope=*/ false);
5661 /* Parse an (optional) statement-seq. */
5662 cp_parser_statement_seq_opt (parser, in_statement_expr_p);
5663 /* Finish the compound-statement. */
5664 finish_compound_stmt (compound_stmt);
5665 /* Consume the `}'. */
5666 cp_parser_require (parser, CPP_CLOSE_BRACE, "`}'");
5667
5668 return compound_stmt;
5669 }
在处理跟在“ { ”后面的符号前,构建了另一个封闭域,这是因为前一个块作用域是为可能的构造函数初始值构建的,而这里构建的才真正为了函数体。
( 点此打开 )
5677 static void
5678 cp_parser_statement_seq_opt (cp_parser* parser, bool in_statement_expr_p) in parser.c
5679 {
5680 /* Scan statements until there aren't any more. */
5681 while (true)
5682 {
5683 /* If we're looking at a `}', then we've run out of statements. */
5684 if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)
5685 || cp_lexer_next_token_is (parser->lexer, CPP_EOF))
5686 break ;
5687
5688 /* Parse the statement. */
5689 cp_parser_statement (parser, in_statement_expr_p);
5690 }
5691 }
深入到调用栈: cp_parser_statement à cp_parser_declaration_statement à cp_parser_block_declaration à cp_parser_simple_declaration ,则 cp_parser_decl_specifier_seq 解析第一个声明的 type-specifier 部分,通过 cp_parser_type_specifier à cp_parser_simple_type_specifier à cp_parser_type_name à cp_parser_class_name 找到对应“ SmallObject<> ”的 template-id 。
5425 static void
5426 cp_parser_statement (cp_parser* parser, bool in_statement_expr_p) in parser.c
5427 {
5428 tree statement;
5429 cp_token *token;
5430 int statement_line_number;
5431
5432 /* There is no statement yet. */
5433 statement = NULL_TREE;
5434 /* Peek at the next token. */
5435 token = cp_lexer_peek_token (parser->lexer);
5436 /* Remember the line number of the first token in the statement. */
5437 statement_line_number = token->location.line;
5438 /* If this is a keyword, then that will often determine what kind of
5439 statement we have. */
5440 if (token->type == CPP_KEYWORD)
5441 {
…
5479 }
5480 else if (token->type == CPP_NAME)
5481 {
5482 /* If the next token is a `:', then we are looking at a
5483 labeled-statement. */
5484 token = cp_lexer_peek_nth_token (parser->lexer, 2);
5485 if (token->type == CPP_COLON)
5486 statement = cp_parser_labeled_statement (parser, in_statement_expr_p);
5487 }
5488 /* Anything that starts with a `{' must be a compound-statement. */
5489 else if (token->type == CPP_OPEN_BRACE)
5490 statement = cp_parser_compound_statement (parser, false);
5491
5492 /* Everything else must be a declaration-statement or an
5493 expression-statement. Try for the declaration-statement
5494 first, unless we are looking at a `;', in which case we know that
5495 we have an expression-statement. */
5496 if (!statement)
5497 {
5498 if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
5499 {
5500 cp_parser_parse_tentatively (parser);
5501 /* Try to parse the declaration-statement. */
5502 cp_parser_declaration_statement (parser);
5503 /* If that worked, we're done. */
5504 if (cp_parser_parse_definitely (parser))
5505 return ;
5506 }
5507 /* Look for an expression-statement instead. */
5508 statement = cp_parser_expression_statement (parser, in_statement_expr_p);
5509 }
5510
5511 /* Set the line number for the statement. */
5512 if (statement && STATEMENT_CODE_P (TREE_CODE (statement)))
5513 STMT_LINENO (statement) = statement_line_number;
5514 }
一个声明语句向一个作用域中引入了一个或多个新的标识符,它具有形式:
declaration-statement: block-declaration
如果被声明引入的一个标识符先前在一个外部的块中声明了,在当前块的余下部分,该外部的声明被屏蔽,之后它又恢复可见。
具有自动存储周期的变量,在其声明语句每次执行时都得到初始化。在从块中退出时,在该块中声明的具有自动存储周期的变量被摧毁。
有可能跳入一个块中,但不绕过具有初始化的声明。从一个具有自动存储周期的局部变量作用域外跳转到其作用域中的程序是错误的,除非该变量是 POD 类型并且没有初始值。例如:
void f() {
// ...
goto lx; // ill-formed: jump into scope of a
// ...
ly:
X a = 1;
// ...
lx:
goto ly; // OK, jump implies destructor call for a followed by construction
// again immediately following label ly
}
在其他初始化执行前,执行所有具有静态存储周期的局部变量的零初始化( zero-initialization )。 POD 类型的,具有静态存储周期,并且通过常量表达式初始化的局部对象,在进入其包含块前被初始化。允许一个实现提前为其他具有静态存储周期的局部对象执行初始化;在相同条件下,允许一个实现为名字空间中的具有静态存储周期的对象执行静态初始化。否则对象在控制第一次经过其声明时得到初始化;在其初始化完成后,这样的对象被认为是初始化的。如果初始化通过抛出异常而退出,该初始化没有完成,因此在控制下一次进入其声明时,会再进行一次尝试。当对象正在被初始化时,如果控制再一次进入声明,其行为是尚未定义的。例如:
int foo(int i) {
static int s = foo(2*i); // recursive call – undefined
return i+1;
}
具有静态存储周期的局部对象的析构函数仅当该变量被构造后才会得到执行。
6144 static void
6145 cp_parser_declaration_statement (cp_parser* parser) in parser.c
6146 {
6147 /* Parse the block-declaration. */
6148 cp_parser_block_declaration (parser, /*statement_p=*/ true);
6149
6150 /* Finish off the statement. */
6151 finish_stmt ();
6152 }
对于“ SmallObject<> object_; ”,再一次根据 block-declaration 的语法进行解析。经过调用栈 cp_parser_block_declaration à cp_parser_simple_declaration à cp_parser_type_specifier à cp_parser_simple_type_specifier à cp_parser_type_name à cp_parser_class_name ,识别 template-id “ SmallObject ”。
5.12.5.2.2.2.1. 模板具现
5.12.5.2.2.2.1.1. template-id
在此次 cp_parser_class_name 调用中,参数 typename_keyword_p , template_keyword_p , type_p , class_head_p 及 is_declaration 都是 false ;而 check_dependency_p 是 true 。详细的 template-id 的查找过程参考 5.12.4.2.2.2.Template-id 一节。
在 cp_parser_template_id 中,当发现该模板是一个类模板或模板模板参数时,将调用 finish_template_type 来完成该类型的处理。对于我们的例子,在 finish_template_type 中调用的 lookup_template_class 的参数 in_decl 及 context 都是 NULL ,代表模板实参列表的 arglist 也是 NULL , dl 则是对应的 TEMPLATE_DECL 节点,而 entering_scope 为非 0 值,表示进入 template-id 所代表的作用域。
4133 tree
4134 lookup_template_class (tree d1, in pt.c
4135 tree arglist,
4136 tree in_decl,
4137 tree context,
4138 int entering_scope,
4139 tsubst_flags_t complain)
4140 {
4141 tree template = NULL_TREE, parmlist;
4142 tree t;
4143
4144 timevar_push (TV_NAME_LOOKUP);
4145
4146 if (TREE_CODE (d1) == IDENTIFIER_NODE)
4147 {
…
4162 }
4163 else if (TREE_CODE (d1) == TYPE_DECL && IS_AGGR_TYPE (TREE_TYPE (d1)))
4164 {
…
4177 }
4178 else if (TREE_CODE (d1) == ENUMERAL_TYPE
4179 || (TYPE_P (d1) && IS_AGGR_TYPE (d1)))
4180 {
…
4183 }
4184 else if (TREE_CODE (d1) == TEMPLATE_DECL
4185 && TREE_CODE (DECL_TEMPLATE_RESULT (d1)) == TYPE_DECL)
4186 {
4187 template = d1;
4188 d1 = DECL_NAME (template);
4189 context = DECL_CONTEXT (template);
4190 }
…
4219 complain &= ~tf_user;
4220
4221 if (DECL_TEMPLATE_TEMPLATE_PARM_P (template))
4222 {
…
4259 }
4260 else
4261 {
4262 tree template_type = TREE_TYPE (template);
4263 tree gen_tmpl;
4264 tree type_decl;
4265 tree found = NULL_TREE;
4266 tree *tp;
4267 int arg_depth;
4268 int parm_depth;
4269 int is_partial_instantiation;
4270
4271 gen_tmpl = most_general_template (template);
4272 parmlist = DECL_TEMPLATE_PARMS (gen_tmpl);
4273 parm_depth = TMPL_PARMS_DEPTH (parmlist);
4274 arg_depth = TMPL_ARGS_DEPTH (arglist);
4275
4276 if (arg_depth == 1 && parm_depth > 1)
4277 {
…
4294 }
4295
4296 /* Now we should have enough arguments. */
4297 my_friendly_assert (parm_depth == arg_depth, 0);
4298
4299 /* From here on, we're only interested in the most general
4300 template. */
4301 template = gen_tmpl;
4302
4303 /* Calculate the BOUND_ARGS. These will be the args that are
4304 actually tsubst'd into the definition to create the
4305 instantiation. */
4306 if (parm_depth > 1)
4307 {
…
4344 }
4345 else
4346 arglist
4347 = coerce_template_parms (INNERMOST_TEMPLATE_PARMS (parmlist),
4348 INNERMOST_TEMPLATE_ARGS (arglist),
4349 template,
4350 complain, /*require_all_args=*/ 1);
4351
4352 if (arglist == error_mark_node)
4353 /* We were unable to bind the arguments. */
4354 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
5.12.5.2.2.2.1.1.1. 替换第一个缺省实参
因为 arglist 是 NULL ,在 4348 行的 INNERMOST_TEMPLATE_ARGS 返回结果 NULL 。而 template 本身是最通用的形式,因此 INNERMOST_TEMPLATE_PARMS 返回了下图中的 TREE_VEC 作为其参数。
( 点此打开 )
3805 static tree
3806 coerce_template_parms (tree parms, in pt.c
3807 tree args,
3808 tree in_decl,
3809 tsubst_flags_t complain,
3810 int require_all_arguments)
3811 {
3812 int nparms, nargs, i, lost = 0;
3813 tree inner_args;
3814 tree new_args;
3815 tree new_inner_args;
3816
3817 inner_args = INNERMOST_TEMPLATE_ARGS (args);
3818 nargs = inner_args ? NUM_TMPL_ARGS (inner_args) : 0;
3819 nparms = TREE_VEC_LENGTH (parms);
3820
3821 if (nargs > nparms
3822 || (nargs < nparms
3823 && require_all_arguments
3824 && TREE_PURPOSE (TREE_VEC_ELT (parms, nargs)) == NULL_TREE))
3825 {
3826 if (complain & tf_error)
3827 {
3828 error ("wrong number of template arguments (%d, should be %d)",
3829 nargs, nparms);
3830
3831 if (in_decl)
3832 cp_error_at ("provided for `%D'", in_decl);
3833 }
3834
3835 return error_mark_node;
3836 }
3837
3838 new_inner_args = make_tree_vec (nparms);
3839 new_args = add_outermost_template_args (args, new_inner_args);
3840 for (i = 0; i < nparms; i++)
3841 {
3842 tree arg;
3843 tree parm;
3844
3845 /* Get the Ith template parameter. */
3846 parm = TREE_VEC_ELT (parms, i);
3847
3848 /* Calculate the Ith argument. */
3849 if (i < nargs)
3850 arg = TREE_VEC_ELT (inner_args, i);
3851 else if (require_all_arguments)
3852 /* There must be a default arg in this case. */
3853 arg = tsubst_template_arg (TREE_PURPOSE (parm), new_args,
3854 complain, in_decl);
3855 else
3856 break ;
3857
3858 my_friendly_assert (arg, 20030727);
3859 if (arg == error_mark_node)
3860 error ("template argument %d is invalid", i + 1);
3861 else
3862 arg = convert_template_argument (TREE_VALUE (parm),
3863 arg, new_args, complain, i,
3864 i n_decl);
3865
3866 if (arg == error_mark_node)
3867 lost++;
3868 TREE_VEC_ELT (new_inner_args, i) = arg;
3869 }
3870
3871 if (lost)
3872 return error_mark_node;
3873
3874 return new_inner_args;
3875 }
在上面的 3839 行因为 args 是 NULL , add_outermost_template_args 不做任何事只是返回 new_inner_args 。在上图中,看到第一个参数的 TREE_PURPOSE 域(对应缺省实参)是“ SingleThreaded ”的 TMEPLATE_DECL 。那么接下来就是实参替换。
5700 static tree
5701 tsubst_template_arg (tree t, tree args, tsubst_flags_t complain, tree in_decl) in pt.c
5702 {
5703 tree r;
5704
5705 if (!t)
5706 r = t;
5707 else if (TYPE_P (t))
5708 r = tsubst (t, args, complain, in_decl);
5709 else
5710 {
5711 r = tsubst_expr (t, args, complain, in_decl);
5712
5713 if (!uses_template_parms (r))
5714 {
5715 /* Sometimes, one of the args was an expression involving a
5716 template constant parameter, like N - 1. Now that we've
5717 tsubst'd, we might have something like 2 - 1. This will
5718 confuse lookup_template_class, so we do constant folding
5719 here. We have to unset processing_template_decl, to fool
5720 tsubst_copy_and_build() into building an actual tree. */
5721
5722 /* If the TREE_TYPE of ARG is not NULL_TREE, ARG is already
5723 as simple as it's going to get, and trying to reprocess
5724 the trees will break. Once tsubst_expr et al DTRT for
5725 non-dependent exprs, this code can go away, as the type
5726 will always be set. */
5727 if (!TREE_TYPE (r))
5728 {
5729 int saved_processing_template_decl = processing_template_decl;
5730 processing_template_decl = 0;
5731 r = tsubst_copy_and_build (r, /*args=*/ NULL_TREE,
5732 tf_error, /*in_decl=*/ NULL_TREE,
5733 /*function_p=*/ false);
5734 processing_template_decl = saved_processing_template_decl;
5735 }
5736 r = fold (r);
5737 }
5738 }
5739 return r;
5740 }
作为一个满足 DECL_P 条件的节点,对其处理由 tsubst_expr 完成。
7822 static tree
7823 tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) in pt.c
7824 {
7825 tree stmt, tmp;
7826 tsubst_flags_t stmt_expr
7827 = complain & (tf_stmt_expr_cmpd | tf_stmt_expr_body);
7828
7829 complain ^= stmt_expr;
7830 if (t == NULL_TREE || t == error_mark_node)
7831 return t;
7832
7833 if (!STATEMENT_CODE_P (TREE_CODE (t)))
7834 return tsubst_copy_and_build (t, args, complain, in_decl,
7835 /*function_p=*/ false);
…
8156 }
上面的 STATEMENT_CODE_P 返回非 0 值,如果节点的编码显示该节点代表一条语句( statement )(显然, TEMPLATE_DECL 不合格;参考 4.3.1.1. stmt_codes 一节)。
8180 tree
8181 tsubst_copy_and_build (tree t, in pt.c
8182 tree args,
8183 tsubst_flags_t complain,
8184 tree in_decl,
8185 bool function_p)
8186 {
8187 #define RECUR(NODE) /
8188 tsubst_copy_and_build (NODE, args, complain, in_decl, /*function_p=*/ false)
8189
8190 tree op1;
8191
8192 if (t == NULL_TREE || t == error_mark_node)
8193 return t;
8194
8195 switch (TREE_CODE (t))
8196 {
…
8693 default :
8694 return tsubst_copy (t, args, complain, in_decl);
8695 }
8696
8697 #undef RECUR
8698 }
在 tsubst_copy_and_build 中, TEMPLATE_DECL 不要求特殊的处理(仅“拷贝”就足够)。
7449 static tree
7450 tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) in pt.c
7451 {
7452 enum tree_code code;
7453 tree r;
7454
7455 if (t == NULL_TREE || t == error_mark_node)
7456 return t;
7457
7458 code = TREE_CODE (t);
7459
7460 switch (code)
7461 {
…
7542 case TEMPLATE_DECL:
7543 if (DECL_TEMPLATE_TEMPLATE_PARM_P (t))
7544 return tsubst (TREE_TYPE (DECL_TEMPLATE_RESULT (t)),
7545 args, complain, in_decl);
7546 else if (is_member_template (t))
7547 return tsubst (t, args, complain, in_decl);
7548 else if (DECL_CLASS_SCOPE_P (t)
7549 && uses_template_parms (DECL_CONTEXT (t)))
7550 {
7551 /* Template template argument like the following example need
7552 special treatment:
7553
7554 template <template <class> class TT> struct C {};
7555 template <class T> struct D {
7556 template <class U> struct E {};
7557 C<E> c; // #1
7558 };
7559 D<int> d; // #2
7560
7561 We are processing the template argument `E' in #1 for
7562 the template instantiation #2. Originally, `E' is a
7563 TEMPLATE_DECL with `D<T>' as its DECL_CONTEXT. Now we
7564 have to substitute this with one having context `D<int>'. */
7565
7566 tree context = tsubst (DECL_CONTEXT (t), args, complain, in_decl);
7567 return lookup_field (context, DECL_NAME(t), 0, false);
7568 }
7569 else
7570 /* Ordinary template template argument. */
7571 return t;
…
7816 }
7817 }
“ SingleThreaded ”没在模板参数列表中定义, DECL_TEMPLATE_TEMPLATE_PARM_P 则返回 0 ;它也不是成员模板;而且其上下文(封闭域)是名字空间“ Loki ”,明显不是类型依赖或值依赖的(因此 uses_template_parms 返回 false );所以在 7571 行返回了对应的 TEMPLATE_DECL 节点。
在回到 tsubst_template_arg 后, TEMPLATE_DECL 因为不是模板模板参数,导致在 5713 行的 uses_template_parms 返回 false ,并且跳过 5727 行的 IF 块,因为其 type 域不是 NULL 。接着的 fold 不做任何事,因为没有常量可折叠,只是返回这个 TEMPLATE_DECL 节点。
在返回缺省实参后,检查是否需要进行调整。在下图中的 TEMPLATE_DECL 节点被 parm 所指向,该节点来自模板参数的定义。
( 点此打开 )
在下图中, TEMPLATE_DECL 节点为 arg 所引用,该节点来自“ SingleThreaded ”的定义。
( 点此打开 )
3636 static tree
3637 convert_template_argument (tree parm, in pt.c
3638 tree arg,
3639 tree args,
3640 tsubst_flags_t complain,
3641 int i,
3642 tree in_decl)
3643 {
3644 tree val;
3645 tree inner_args;
3646 int is_type, requires_type, is_tmpl_type, requires_tmpl_type;
3647
3648 inner_args = INNERMOST_TEMPLATE_ARGS (args);
3649
3650 requires_tmpl_type = TREE_CODE (parm) == TEMPLATE_DECL;
3651 requires_type = (TREE_CODE (parm) == TYPE_DECL
3652 || requires_tmpl_type);
3653
3654 is_tmpl_type = ((TREE_CODE (arg) == TEMPLATE_DECL
3655 && TREE_CODE (DECL_TEMPLATE_RESULT (arg)) == TYPE_DECL)
3656 || TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM
3657 || TREE_CODE (arg) == UNBOUND_CLASS_TEMPLATE);
3658
3659 if (is_tmpl_type
3660 && (TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM
3661 || TREE_CODE (arg) == UNBOUND_CLASS_TEMPLATE))
3662 arg = TYPE_STUB_DECL (arg);
3663
3664 is_type = TYPE_P (arg) || is_tmpl_type;
…
3722 if (is_type)
3723 {
3724 if (requires_tmpl_type)
3725 {
3726 if (TREE_CODE (TREE_TYPE (arg)) == UNBOUND_CLASS_TEMPLATE)
3727 /* The number of argument required is not known yet.
3728 Just accept it for now. */
3729 val = TREE_TYPE (arg);
3730 else
3731 {
3732 tree parmparm = DECL_INNERMOST_TEMPLATE_PARMS (parm);
3733 tree argparm = DECL_INNERMOST_TEMPLATE_PARMS (arg);
3734
3735 if (coerce_template_template_parms (parmparm, argparm,
3736 complain, in_decl,
3737 inner_args))
3738 {
3739 val = arg;
3740
3741 /* TEMPLATE_TEMPLATE_PARM node is preferred over
3742 TEMPLATE_DECL. */
3743 if (val != error_mark_node
3744 && DECL_TEMPLATE_TEMPLATE_PARM_P (val))
3745 val = TREE_TYPE (val);
3746 }
3747 else
3748 {
…
3757 }
3758 }
3759 }
3760 else
3761 val = groktypename (arg);
3762 }
3763 else
3764 {
…
3790 }
3791
3792 return val;
3793 }
明显地,在 3732 行, parmparm 及 argparm 分别引用各自图中,对应 TREE_VEC 中的节点。在 coerce_template_template_parms 中不做任何事 , 并返回 1 。因此 arg 被 convert_template_argument 返回,在 coerce_template_parms 中,它被设置为 new_inner_args 的第一个元素。