在下面默认的, flag_elide_constructor 是 1 ,这表示尽可能消减构造函数。构造函数(包括 operator= (const X&) ,如果定义了)是否可以被削减,依赖于它是否是平凡的。断言 TYPE_HAS_COMPLEX_INIT_REF 成立,如果该类类型具有非平凡拷贝构造函数;而断言 TYPE_HAS_COMPLEX_ASSIGN_REF 成立,如果该类类型具有非平凡的“ operator = (const X&) ”。
build_over_call (continue)
4534 if (warn_format )
4535 check_function_format (NULL, TYPE_ATTRIBUTES (TREE_TYPE (fn)),
4536 converted_args);
4537
4538 /* Avoid actually calling copy constructors and copy assignment operators,
4539 if possible. */
4540
4541 if (! flag_elide_constructors )
4542 /* Do things the hard way. */ ;
4543 else if (TREE_VEC_LENGTH (convs) == 1
4544 && DECL_COPY_CONSTRUCTOR_P (fn))
4545 {
4546 tree targ;
4547 arg = skip_artificial_parms_for (fn, converted_args);
4548 arg = TREE_VALUE (arg);
4549
4550 /* Pull out the real argument, disregarding const-correctness. */
4551 targ = arg;
4552 while (TREE_CODE (targ) == NOP_EXPR
4553 || TREE_CODE (targ) == NON_LVALUE_EXPR
4554 || TREE_CODE (targ) == CONVERT_EXPR)
4555 targ = TREE_OPERAND (targ, 0);
4556 if (TREE_CODE (targ) == ADDR_EXPR)
4557 {
4558 targ = TREE_OPERAND (targ, 0);
4559 if (!same_type_ignoring_top_level_qualifiers_p
4560 (TREE_TYPE (TREE_TYPE (arg)), TREE_TYPE (targ)))
4561 targ = NULL_TREE;
4562 }
4563 else
4564 targ = NULL_TREE;
4565
4566 if (targ)
4567 arg = targ;
4568 else
4569 arg = build_indirect_ref (arg, 0);
4570
4571 /* [class.copy]: the copy constructor is implicitly defined even if
4572 the implementation elided its use. */
4573 if (TYPE_HAS_COMPLEX_INIT_REF (DECL_CONTEXT (fn)))
4574 mark_used (fn);
4575
4576 /* If we're creating a temp and we already have one, don't create a
4577 new one. If we're not creating a temp but we get one, use
4578 INIT_EXPR to collapse the temp into our target. Otherwise, if the
4579 ctor is trivial, do a bitwise copy with a simple TARGET_EXPR for a
4580 temp or an INIT_EXPR otherwise. */
4581 if (integer_zerop (TREE_VALUE (args)))
4582 {
4583 if (TREE_CODE (arg) == TARGET_EXPR)
4584 return arg;
4585 else if (TYPE_HAS_TRIVIAL_INIT_REF (DECL_CONTEXT (fn)))
4586 return build_target_expr_with_type (arg, DECL_CONTEXT (fn));
4587 }
4588 else if (TREE_CODE (arg) == TARGET_EXPR
4589 || TYPE_HAS_TRIVIAL_INIT_REF (DECL_CONTEXT (fn)))
4590 {
4591 tree to = stabilize_reference
4592 (build_indirect_ref (TREE_VALUE (args), 0));
4593
4594 val = build (INIT_EXPR, DECL_CONTEXT (fn), to, arg);
4595 return val;
4596 }
4597 }
根据【 3 】,条文 12.8 “拷贝类对象”,条款 2 :
一个非模板的构造函数,对于类 X 是一个拷贝构造函数,如果其第一个形参具有类型 X& , const X& , volatile X& 或 const volatile X& ,并且要么没有其它形参,或其它形参都具有缺省实参( 8.3.6 )。 [ 例子: X::X(const X&) 及 X::X(X&, int=1) 都是拷贝构造函数。
class X {
public :
X(int);
X(const X&, int = 1);
};
X a(1); // calls X(int);
X b(a, 0); // calls X(const X&, int);
X c = b; // calls X(const X&, int);
— 例子结束 ]
前面看到, 4543 行“ TREE_VEC_LENGTH (convs) ”的值实际上反映了函数的调用实参的数目(加上返回值);因为构造函数没有返回值,这里 1 表示调用中传给该拷贝构造函数的实参数目是 1 。
来到这里之前,实参经过了前面章节所述的转换,一般而言,可以直接为之构建一个 INDIRECT_REF 节点来代表引用( & ),除非先前已是引用。 4552 行的 WHILE 循环与 4556 行的 IF 语句就是判断这个引用,考虑下面的例子:
void f (char& t) {}
void g (int& b) { f (*(char*)b); }
int main () {
int b = 5;
g (b);
}
在 4560 行“ TREE_TYPE (TREE_TYPE (arg)) ”得到的类型是“ char ”,而“ TREE_TYPE (targ) ”则是“ int ”,这个引用类型“ int& b ”不可直接使用,需要构建“ char& ”的类型。
在 4573 行,如果 TYPE_HAS_COMPLEX_INIT_REF 成立,意味着对应的类定义了拷贝构造函数,或具有虚拟基类,或具有虚拟函数(显然,这个拷贝构造函数不会是平凡的),调用 mark_used 来标记这个拷贝构造函数为被使用,并且如果使用者没有声明,而是由编译器“人工”产生拷贝构造函数,合成它的定义。
在 4581 行, args 是调用的实参。它是应该 tree_list ,其中每个节点的 TREE_VALUE 域保存对应的实参。在上面的例子中,在“ X b(a, 0) ”中的“ TREE_VALUE (args) ”是隐含的 this 指针,它是 0 (满足 4581 行的条件);而在“ X c = b ”中,它是“ &c ”。对于前一个情形,需要构建新的对象(由于 C++ 语法的限制,它只能是函数内的临时对象);而对于后一个情形,我们可以使用‘ c ’作为 INIT_EXPR 的对象。在 TYPE_HAS_TRIVIAL_INIT_REF 成立,即可以使用按位拷贝来拷贝对象时,我们可以忽略拷贝构造函数。如果拷贝构造函数不能忽略,我们将去到下面的 4637 行来处理。
而在下面的 4599 行, copy_fn_p 返回非 0 值,如果 fn 是构造函数或重载的操作符“ = ”;而 4598 行的 DECL_OVERLOADED_OPERATOR_P 成立,如果 fn 是重载的操作符“ = ”;因此进入 4601 行的代码块的条件是: fn 是重载操作符“ = ”,但该类对象间的赋值可以通过按位拷贝完成(满足 4600 行条件)。
build_over_call (continue)
4598 else if (DECL_OVERLOADED_OPERATOR_P (fn) == NOP_EXPR
4599 && copy_fn_p (fn)
4600 && TYPE_HAS_TRIVIAL_ASSIGN_REF (DECL_CONTEXT (fn)))
4601 {
4602 tree to = stabilize_reference
4603 (build_indirect_ref (TREE_VALUE (converted_args), 0));
4604 tree type = TREE_TYPE (to);
4605 tree as_base = CLASSTYPE_AS_BASE (type);
4606
4607 arg = build_indirect_ref (TREE_VALUE (TREE_CHAIN (converted_args)), 0);
4608 if (tree_int_cst_equal (TYPE_SIZE (type), TYPE_SIZE (as_base)))
4609 val = build (MODIFY_EXPR, TREE_TYPE (to), to, arg);
4610 else
4611 {
4612 /* We must only copy the non-tail padding parts. Use
4613 CLASSTYPE_AS_BASE for the bitwise copy. */
4614 tree to_ptr, arg_ptr, to_as_base, arg_as_base, base_ptr_type;
4615 tree save_to;
4616
4617 to_ptr = save_expr (build_unary_op (ADDR_EXPR, to, 0));
4618 arg_ptr = build_unary_op (ADDR_EXPR, arg, 0);
4619
4620 base_ptr_type = build_pointer_type (as_base);
4621 to_as_base = build_nop (base_ptr_type, to_ptr);
4622 to_as_base = build_indirect_ref (to_as_base, 0);
4623 arg_as_base = build_nop (base_ptr_type, arg_ptr);
4624 arg_as_base = build_indirect_ref (arg_as_base, 0);
4625
4626 save_to = build_indirect_ref (to_ptr, 0);
4627
4628 val = build (MODIFY_EXPR, as_base, to_as_base, arg_as_base);
4629 val = convert_to_void (val, NULL);
4630 val = build (COMPOUND_EXPR, type, val, save_to);
4631 TREE_NO_UNUSED_WARNING (val) = 1;
4632 }
4633
4634 return val;
4635 }
4602 行从这个 this 指针获取了该类类型。在 4605 行的 CLASSTYPE_AS_BASE 返回了没有虚拟基类部分的类基干( class base );不过因为该拷贝构造函数是平凡的,这意味着这个类不包含任何虚拟基类。被返回的类基干应该与类的大小相同,除非这个类有填充字节(在 layout_class_type 中看到,类基干的大小不包括填充字节)。填充字节不应该纳入按位拷贝。
如果拷贝构造函数或重载的操作符“ = ”不是平凡的,则需要下面的处理来产生调用对应函数的代码。
build_over_call (continue)
4637 mark_used (fn);
4638
4639 if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0)
4640 {
4641 tree t, *p = &TREE_VALUE (converted_args);
4642 tree binfo = lookup_base (TREE_TYPE (TREE_TYPE (*p)),
4643 DECL_CONTEXT (fn),
4644 ba_any, NULL);
4645 my_friendly_assert (binfo && binfo != error_mark_node, 20010730);
4646
4647 *p = build_base_path (PLUS_EXPR, *p, binfo, 1);
4648 if (TREE_SIDE_EFFECTS (*p))
4649 *p = save_expr (*p);
4650 t = build_pointer_type (TREE_TYPE (fn));
4651 if (DECL_CONTEXT (fn) && TYPE_JAVA_INTERFACE (DECL_CONTEXT (fn)))
4652 fn = build_java_interface_fn_ref (fn, *p);
4653 else
4654 fn = build_vfn_ref (build_indirect_ref (*p, 0), DECL_VINDEX (fn));
4655 TREE_TYPE (fn) = t;
4656 }
4657 else if (DECL_INLINE (fn))
4658 fn = inline_conversion (fn);
4659 else
4660 fn = build_addr_func (fn);
4661
4662 return build_cxx_call (fn, args, converted_args);
4663 }
如果被调用的函数不是虚函数,就获取其地址并产生调用它的代码。对于内联函数,注意到内联函数,除了为引用或指针所援引的那些之外,不应该设置 TREE_ADDRESSABLE (这个位由 build_addr_func 设置),因为它总是在调用点展开。
1464 tree
1465 inline_conversion (tree exp) in typeck.c
1466 {
1467 if (TREE_CODE (exp) == FUNCTION_DECL)
1468 exp = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (exp)), exp);
1469
1470 return exp;
1471 }
而如果这个函数是虚函数,首先需要检查定义了这个函数的基类是否可以由 p 指定的类来访问。然后调整“ this ”指针指向这个基类。接着我们将从 vtable 中获取这个虚函数的地址。注意下面的 instance 是一个 INDIRECT_REF 节点。
470 tree
471 build_vfn_ref (tree instance, tree idx) in class.c
472 {
473 tree aref = build_vtbl_ref_1 (instance, idx);
474
475 /* When using function descriptors, the address of the
476 vtable entry is treated as a function pointer. */
477 if (TARGET_VTABLE_USES_DESCRIPTORS)
478 aref = build1 (NOP_EXPR, TREE_TYPE (aref),
479 build_unary_op (ADDR_EXPR, aref, /*noconvert=*/ 1));
480
481 return aref;
482 }
回忆下面 446 行的“ BINFO_VTABLE (binfo) ”返回基类 binfo 的 vtable 。因为 binfo 来自最后派生类的 binfo 里,在前面我们已经看到,这样返回的 vtable 是该派生类 vtable 的一部分。而在 437 行, fixed_type_or_null 将返回 NULL ,因为 instance 是一个指针加法, fixed_type_or_null 并不能确定其动态类型。接着 build_vfield_ref 将为这个 vtable 构建 COMPONENT_REF 来代表对其的访问。
427 ic tree
428 build_vtbl_ref_1 (tree instance, tree idx) in class.c
429 {
430 tree aref;
431 tree vtbl = NULL_TREE;
432
433 /* Try to figure out what a reference refers to, and
434 access its virtual function table directly. */
435
436 int cdtorp = 0;
437 tree fixed_type = fixed_type_or_null (instance, NULL, &cdtorp);
438
439 tree basetype = non_reference (TREE_TYPE (instance));
440
441 if (fixed_type && !cdtorp)
442 {
443 tree binfo = lookup_base (fixed_type, basetype,
444 ba_ignore|ba_quiet, NULL);
445 if (binfo)
446 vtbl = BINFO_VTABLE (binfo);
447 }
448
449 if (!vtbl)
450 vtbl = build_vfield_ref (instance, basetype);
451
452 assemble_external (vtbl);
453
454 aref = build_array_ref (vtbl, idx);
455
456 return aref;
457 }
最后,访问 vtable 指定索引处,可以得到期望的虚函数地址,在上面 4662 行对其调用的代码由 build_cxx_call 产生。
在一个 IDENTITY_CONV 或 BASE_CONV 中,如果拷贝构造函数必须是可访问的,即便不使用它, CHECK_COPY_CONSTRUCTOR_P 就是 true 。记得序列中的最后一个转换总是 IDENTITY_CONV 。
而如果我们看到一个具有二义性的转换,能来到这里,它是通过隐式转换进来的( implicit_conversion )。彼时,在该函数中调用 build_user_type_conversion_1 的 flags 实参为 LOOKUP_ONLYCONVERTING ,它不会输出二义性出错消息,因此以 LOOKUP_NORMAL 再调用一次,给出错误消息(这是转换发生点,在这里给出消息最准确)。
convert_like_real (continue)
4017 case IDENTITY_CONV:
4018 if (type_unknown_p (expr))
4019 expr = instantiate_type (totype, expr, tf_error | tf_warning);
4020 /* Convert a non-array constant variable to its underlying
4021 value, unless we are about to bind it to a reference, in
4022 which case we need to leave it as an lvalue. */
4023 if (inner >= 0
4024 && TREE_CODE (TREE_TYPE (expr)) != ARRAY_TYPE)
4025 expr = decl_constant_value (expr);
4026 if (CHECK_COPY_CONSTRUCTOR_P (convs))
4027 check_constructor_callable (totype, expr);
4028
4029 return expr;
4030 case AMBIG_CONV:
4031 /* Call build_user_type_conversion again for the error. */
4032 return build_user_type_conversion
4033 (totype, TREE_OPERAND (convs, 0), LOOKUP_NORMAL);
4034
4035 default :
4036 break ;
4037 };
注意到在上面, USER_CONV , IDENTITY_CONV 或 AMBIG_CONV 都在它们的 CASE 块后从函数返回;来到下面的代码,它将是其他的转换。注意 4039 行的递归,这是这个函数中我们可以深入序列的唯一的地方。显然, USER_CONV , IDENTITY_CONV ,及 AMBIG_CONV 都是递归的出口。这是因为这个序列把其中的转换以反序链接(先目标,后源)。看到不是序列中所有的转换都会被访问到,例如,在用户定义转换序列中,总是在最后的 IDENTITY_CONV 将被跳过,因为递归在 USER_CONV 处终止。
convert_like_real (continue)
4039 expr = convert_like_real (TREE_OPERAND (convs, 0), expr, fn, argnum,
4040 TREE_CODE (convs) == REF_BIND ? -1 : 1,
4041 /*issue_conversion_warnings=*/ false);
4042 if (expr == error_mark_node)
4043 return error_mark_node;
4044
4045 switch (TREE_CODE (convs))
4046 {
4047 case RVALUE_CONV:
4048 if (! IS_AGGR_TYPE (totype))
4049 return expr;
4050 /* Else fall through. */
4051 case BASE_CONV:
4052 if (TREE_CODE (convs) == BASE_CONV && !NEED_TEMPORARY_P (convs))
4053 {
4054 /* We are going to bind a reference directly to a base-class
4055 subobject of EXPR. */
4056 if (CHECK_COPY_CONSTRUCTOR_P (convs))
4057 check_constructor_callable (TREE_TYPE (expr), expr);
4058 /* Build an expression for `*((base*) &expr)'. */
4059 expr = build_unary_op (ADDR_EXPR, expr, 0);
4060 expr = perform_implicit_conversion (build_pointer_type (totype),
4061 expr);
4062 expr = build_indirect_ref (expr, "implicit conversion");
4063 return expr;
4064 }
4065
4066 /* Copy-initialization where the cv-unqualified version of the source
4067 type is the same class as, or a derived class of, the class of the
4068 destination [is treated as direct-initialization]. [dcl.init] */
4069 expr = build_temp (expr, totype, LOOKUP_NORMAL|LOOKUP_ONLYCONVERTING,
4070 &diagnostic_fn);
4071 if (diagnostic_fn && fn)
4072 diagnostic_fn (" initializing argument %P of `%D'", argnum, fn);
4073 return build_cplus_new (totype, expr);
4074
4075 case REF_BIND:
4076 {
4077 tree ref_type = totype;
4078
4079 /* If necessary, create a temporary. */
4080 if (NEED_TEMPORARY_P (convs) || !lvalue_p (expr))
4081 {
4082 tree type = TREE_TYPE (TREE_OPERAND (convs, 0));
4083 cp_lvalue_kind lvalue = real_lvalue_p (expr);
4084
4085 if (!CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (ref_type)))
4086 {
4087 /* If the reference is volatile or non-const, we
4088 cannot create a temporary. */
4089 if (lvalue & clk_bitfield)
4090 error ("cannot bind bitfield `%E' to `%T'",
4091 expr, ref_type);
4092 else if (lvalue & clk_packed)
4093 error ("cannot bind packed field `%E' to `%T'",
4094 expr, ref_type);
4095 else
4096 error ("cannot bind rvalue `%E' to `%T'", expr, ref_type);
4097 return error_mark_node;
4098 }
4099 /* If the source is a packed field, and we must use a copy
4100 constructor, then building the target expr will require
4101 binding the field to the reference parameter to the
4102 copy constructor, and we'll end up with an infinite
4103 loop. If we can use a bitwise copy, then we'll be
4104 OK. */
4105 if ((lvalue & clk_packed)
4106 && CLASS_TYPE_P (type)
4107 && !TYPE_HAS_TRIVIAL_INIT_REF (type))
4108 {
4109 error ("cannot bind packed field `%E' to `%T'",
4110 expr, ref_type);
4111 return error_mark_node;
4112 }
4113 expr = build_target_expr_with_type (expr, type);
4114 }
4115
4116 /* Take the address of the thing to which we will bind the
4117 reference. */
4118 expr = build_unary_op (ADDR_EXPR, expr, 1);
4119 if (expr == error_mark_node)
4120 return error_mark_node;
4121
4122 /* Convert it to a pointer to the type referred to by the
4123 reference. This will adjust the pointer if a derived to
4124 base conversion is being performed. */
4125 expr = cp_convert (build_pointer_type (TREE_TYPE (ref_type)),
4126 expr);
4127 /* Convert the pointer to the desired reference type. */
4128 return build_nop (ref_type, expr);
4129 }
4130
4131 case LVALUE_CONV:
4132 return decay_conversion (expr);
4133
4134 case QUAL_CONV:
4135 /* Warn about deprecated conversion if appropriate. */
4136 string_conv_p (totype, expr, 1);
4137 break ;
4138
4139 default :
4140 break ;
4141 }
4142 return ocp_convert (totype, expr, CONV_IMPLICIT,
4143 LOOKUP_NORMAL|LOOKUP_NO_CONVERSION);
4144 }
显然,这里 LVALUE_CONV , RVALUE_CONV , REF_BIND , QUAL_CONV ,及 BASE_CONV 则按照从源类型到目标类型的转换步骤执行。而 PTR_CONV , QUAL_CONV ,及 PMEM_CONV 则是直接由下面的函数处理。
下面的参数 convtype 是一个比特位的集合,其中位 CONV_FORCE_TEMP 表示当转换到同一个聚合类型时,要求一个新的临时对象(不过在我们这里的调用上下文中,这个参数是 CONV_IMPLICIT ,它表示执行隐式转换)。这个函数把通过参数 expr 传入的表达式转换到由参数 type 指定的类型。
614 tree
615 ocp_convert (tree type, tree expr, int convtype, int flags) in cvt.c
616 {
617 tree e = expr;
618 enum tree_code code = TREE_CODE (type);
619
620 if (error_operand_p (e) || type == error_mark_node)
621 return error_mark_node;
622
623 complete_type (type);
624 complete_type (TREE_TYPE (expr));
625
626 e = decl_constant_value (e);
627
628 if (IS_AGGR_TYPE (type) && (convtype & CONV_FORCE_TEMP)
629 /* Some internal structures (vtable_entry_type, sigtbl_ptr_type)
630 don't go through finish_struct, so they don't have the synthesized
631 constructors. So don't force a temporary. */
632 && TYPE_HAS_CONSTRUCTOR (type))
633 /* We need a new temporary; don't take this shortcut. */ ;
634 else if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (e)))
635 {
636 if (same_type_p (type, TREE_TYPE (e)))
637 /* The call to fold will not always remove the NOP_EXPR as
638 might be expected, since if one of the types is a typedef;
639 the comparison in fold is just equality of pointers, not a
640 call to comptypes. We don't call fold in this case because
641 that can result in infinite recursion; fold will call
642 convert, which will call ocp_convert, etc. */
643 return e;
644 /* For complex data types, we need to perform componentwise
645 conversion. */
646 else if (TREE_CODE (type) == COMPLEX_TYPE)
647 return fold (convert_to_complex (type, e));
648 else if (TREE_CODE (e) == TARGET_EXPR)
649 {
650 /* Don't build a NOP_EXPR of class type. Instead, change the
651 type of the temporary. Only allow this for cv-qual changes,
652 though. */
653 if (!same_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (e)),
654 TYPE_MAIN_VARIANT (type)))
655 abort ();
656 TREE_TYPE (e) = TREE_TYPE (TARGET_EXPR_SLOT (e)) = type;
657 return e;
658 }
659 else if (TREE_ADDRESSABLE (type))
660 /* We shouldn't be treating objects of ADDRESSABLE type as rvalues. */
661 abort ();
662 else
663 return fold (build1 (NOP_EXPR, type, e));
664 }
665
666 if (code == VOID_TYPE && (convtype & CONV_STATIC))
667 {
668 e = convert_to_void (e, /*implicit=*/ NULL);
669 return e;
670 }
671
672 if (INTEGRAL_CODE_P (code))
673 {
674 tree intype = TREE_TYPE (e);
675 /* enum = enum, enum = int, enum = float, (enum)pointer are all
676 errors. */
677 if (TREE_CODE (type) == ENUMERAL_TYPE
678 && ((ARITHMETIC_TYPE_P (intype) && ! (convtype & CONV_STATIC))
679 || (TREE_CODE (intype) == POINTER_TYPE)))
680 {
681 pedwarn ("conversion from `%#T' to `%#T'", intype, type);
682
683 if (flag_pedantic_errors )
684 return error_mark_node;
685 }
686 if (IS_AGGR_TYPE (intype))
687 {
688 tree rval;
689 rval = build_type_conversion (type, e);
690 if (rval)
691 return rval;
692 if (flags & LOOKUP_COMPLAIN)
693 error ("`%#T' used where a `%T' was expected", intype, type);
694 if (flags & LOOKUP_SPECULATIVELY)
695 return NULL_TREE;
696 return error_mark_node;
697 }
698 if (code == BOOLEAN_TYPE)
699 return cp_truthvalue_conversion (e);
700
701 return fold (convert_to_integer (type, e));
702 }
703 if (POINTER_TYPE_P (type) || TYPE_PTR_TO_MEMBER_P (type))
704 return fold (cp_convert_to_pointer (type, e, false));
上面,从 672 到 702 行处理到整型的转换。这个函数将被用于 static_cast , const_cast ,及 reinterpret_cast ,它们分别为位 CONV_STATIC , CONV_CONST 及 CONV_REINTERPRET 表示。在上面的代码中,可以清楚地看到这些转换的限制。