在前一节,转换序列被确定;那么执行这个序列,可以从转换中得到目标类型。在 C++ 的前端中,在下面的 convert_like_real 里,转换将真正执行。在其参数中, fn 及 argnum 用于诊断的目的,其中 argnum 是从 0 开始, -1 表示方法的‘ this ’指针实参。而 inner 是非 0 值,当被调用继续一个转换链时。当应用一个引用绑定时,它是负值;否则就是正值。如果 issue_conversion_warnings 是 true ,合适的话,将对可疑的转换发出警告。
首先,如果转换序列是不良的( 3924 行的 ICS_BAD_FLAG 成立),并且是一个标准转换,为了尽可能给出正确的诊断信息,前端仍然会通过递归 convert_like_real 来执行转换序列中正确的部分。
3917 static tree
3918 convert_like_real (tree convs, tree expr, tree fn, int argnum, int inner, in call.c
3919 bool issue_conversion_warnings)
3920 {
3921 tree totype = TREE_TYPE (convs);
3922 void (*diagnostic_fn)(const char *, ...);
3923
3924 if (ICS_BAD_FLAG (convs)
3925 && TREE_CODE (convs) != USER_CONV
3926 && TREE_CODE (convs) != AMBIG_CONV
3927 && TREE_CODE (convs) != REF_BIND)
3928 {
3929 tree t = convs;
3930 for (; t; t = TREE_OPERAND (t, 0))
3931 {
3932 if (TREE_CODE (t) == USER_CONV || !ICS_BAD_FLAG (t))
3933 {
3934 expr = convert_like_real (t, expr, fn, argnum, 1,
3935 /*issue_conversion_warnings=*/ false);
3936 break ;
3937 }
3938 else if (TREE_CODE (t) == AMBIG_CONV)
3939 return convert_like_real (t, expr, fn, argnum, 1,
3940 /*issue_conversion_warnings=*/ false);
3941 else if (TREE_CODE (t) == IDENTITY_CONV)
3942 break ;
3943 }
3944 pedwarn ("invalid conversion from `%T' to `%T'", TREE_TYPE (expr), totype);
3945 if (fn)
3946 pedwarn (" initializing argument %P of `%D'", argnum, fn);
3947 return cp_convert (totype, expr);
3948 }
在这里这个不良转换不为标准所允许,但还是为扩展所接受的;否则在前一节就会给出 NULL ,而不是 convs 。扩展的行为只是忽略不良的部分,并发出警告。
在序列中可能有一部分是合法的但包含了潜在的危险性。如果 issue_conversion_warnings 是非 0 值,调用 dubious_conversion_warnings 来做这个检查。
5657 tree
5658 dubious_conversion_warnings (tree type, tree expr, in typeck.c
5659 const char *errtype, tree fndecl, int parmnum)
5660 {
5661 type = non_reference (type);
5662
5663 /* Issue warnings about peculiar, but valid, uses of NULL. */
5664 if (ARITHMETIC_TYPE_P (type) && expr == null_node)
5665 {
5666 if (fndecl)
5667 warning ("passing NULL used for non-pointer %s %P of `%D'",
5668 errtype, parmnum, fndecl);
5669 else
5670 warning ("%s to non-pointer type `%T' from NULL", errtype, type);
5671 }
5672
5673 /* Warn about assigning a floating-point type to an integer type. */
5674 if (TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE
5675 && TREE_CODE (type) == INTEGER_TYPE)
5676 {
5677 if (fndecl)
5678 warning ("passing `%T' for %s %P of `%D'",
5679 TREE_TYPE (expr), errtype, parmnum, fndecl);
5680 else
5681 warning ("%s to `%T' from `%T'", errtype, type, TREE_TYPE (expr));
5682 }
5683 /* And warn about assigning a negative value to an unsigned
5684 variable. */
5685 else if (TREE_UNSIGNED (type) && TREE_CODE (type) != BOOLEAN_TYPE)
5686 {
5687 if (TREE_CODE (expr) == INTEGER_CST
5688 && TREE_NEGATED_INT (expr))
5689 {
5690 if (fndecl)
5691 warning ("passing negative value `%E' for %s %P of `%D'",
5692 expr, errtype, parmnum, fndecl);
5693 else
5694 warning ("%s of negative value `%E' to `%T'",
5695 errtype, expr, type);
5696 }
5697
5698 overflow_warning (expr);
5699
5700 if (TREE_CONSTANT (expr))
5701 expr = fold (expr);
5702 }
5703 return expr;
5704 }
上面对 expr 可能的改变是,如果 expr 是常量,对它执行的常量折叠( constant-folding )。在检查了有危险性的操作后,根据其类型开处理转换。对于用户定义转换序列,它由下面的代码处理。
convert_like_real (continue)
3950 if (issue_conversion_warnings)
3951 expr = dubious_conversion_warnings
3952 (totype, expr, "converting", fn, argnum);
3953 switch (TREE_CODE (convs))
3954 {
3955 case USER_CONV:
3956 {
3957 struct z_candidate *cand = USER_CONV_CAND (convs);
3958 tree convfn = cand->fn;
3959 tree args;
3960
3961 if (DECL_CONSTRUCTOR_P (convfn))
3962 {
3963 tree t = build_int_2 (0, 0);
3964 TREE_TYPE (t) = build_pointer_type (DECL_CONTEXT (convfn));
3965
3966 args = build_tree_list (NULL_TREE, expr);
3967 if (DECL_HAS_IN_CHARGE_PARM_P (convfn)
3968 || DECL_HAS_VTT_PARM_P (convfn))
3969 /* We should never try to call the abstract or base constructor
3970 from here. */
3971 abort ();
3972 args = tree_cons (NULL_TREE, t, args);
3973 }
3974 else
3975 args = build_this (expr);
3976 expr = build_over_call (cand, LOOKUP_NORMAL);
3977
3978 /* If this is a constructor or a function returning an aggr type,
3979 we need to build up a TARGET_EXPR. */
3980 if (DECL_CONSTRUCTOR_P (convfn))
3981 expr = build_cplus_new (totype, expr);
3982
3983 /* The result of the call is then used to direct-initialize the object
3984 that is the destination of the copy-initialization. [dcl.init]
3985
3986 Note that this step is not reflected in the conversion sequence;
3987 it affects the semantics when we actually perform the
3988 conversion, but is not considered during overload resolution.
3989
3990 If the target is a class, that means call a ctor. */
3991 if (IS_AGGR_TYPE (totype)
3992 && (inner >= 0 || !lvalue_p (expr)))
3993 {
3994 expr = (build_temp
3995 (expr, totype,
3996 /* Core issue 84, now a DR, says that we don't
3997 allow UDCs for these args (which deliberately
3998 breaks copy-init of an auto_ptr<Base> from an
3999 auto_ptr<Derived>). */
4000 LOOKUP_NORMAL|LOOKUP_ONLYCONVERTING|LOOKUP_NO_CONVERSION,
4001 &diagnostic_fn));
4002
4003 if (diagnostic_fn)
4004 {
4005 if (fn)
4006 diagnostic_fn
4007 (" initializing argument %P of `%D' from result of `%D'",
4008 argnum, fn, convfn);
4009 else
4010 diagnostic_fn
4011 (" initializing temporary from result of `%D'", convfn);
4012 }
4013 expr = build_cplus_new (totype, expr);
4014 }
4015 return expr;
4016 }
在前一节中,我们已经看到如果为用户定义转换找到了最佳候选者(以 z_candidate 的形式),一个 WRAPPER 节点被构建来封装找到的 z_candidate ,而它又被作为 convs 的第一个操作数,那么在 3957 行, USER_CONV_CAND 获取了这个 z_candidate 。因此在 3958 行的 convfn 指向对应的函数声明。
很可能构造函数被用于转换,在这个情况下,传入的“ this ”指针应该是 NULL ,而在退出时指向被构建的对象。在实参列表中准备了这个“ this ”指针之后, build_over_call 为调用这个重载构建所需的代码。
在构建转换序列的过程中,如果压制了警告消息的发布,在 joust 中所有的警告信息都会被保存在 4392 行的 warnings 域里;因此如果 warnings 域不是空的,意味着有仍未了结的警告,现在是时候发布这些警告,因为我们就要执行真正的转换了。
在 4396 行, DECL_FUNCTION_MEMBER_P 成立,如果 fn 是应该成员函数(静态或非静态)。对于这个情形,需要通过“ cand->access_path ”——它保存了要访问这个成员函数的基类的 binfo ,来检查可访问性。
4362 static tree
4363 build_over_call (struct z_candidate *cand, int flags) in call.c
4364 {
4365 tree fn = cand->fn;
4366 tree args = cand->args;
4367 tree convs = cand->convs;
4368 tree converted_args = NULL_TREE;
4369 tree parm = TYPE_ARG_TYPES (TREE_TYPE (fn));
4370 tree conv, arg, val;
4371 int i = 0;
4372 int is_method = 0;
4373
4374 /* In a template, there is no need to perform all of the work that
4375 is normally done. We are only interested in the type of the call
4376 expression, i.e., the return type of the function. Any semantic
4377 errors will be deferred until the template is instantiated. */
4378 if (processing_template_decl )
4379 {
4380 tree expr;
4381 tree return_type;
4382 return_type = TREE_TYPE (TREE_TYPE (fn));
4383 expr = build (CALL_EXPR, return_type, fn, args);
4384 if (TREE_THIS_VOLATILE (fn) && cfun )
4385 current_function_returns_abnormally = 1;
4386 if (!VOID_TYPE_P (return_type))
4387 require_complete_type (return_type);
4388 return convert_from_reference (expr);
4389 }
4390
4391 /* Give any warnings we noticed during overload resolution. */
4392 if (cand->warnings)
4393 for (val = cand->warnings; val; val = TREE_CHAIN (val))
4394 joust (cand, WRAPPER_ZC (TREE_VALUE (val)), 1);
4395
4396 if (DECL_FUNCTION_MEMBER_P (fn))
4397 {
4398 /* If FN is a template function, two cases must be considered.
4399 For example:
4400
4401 struct A {
4402 protected:
4403 template <class T> void f();
4404 };
4405 template <class T> struct B {
4406 protected:
4407 void g();
4408 };
4409 struct C : A, B<int> {
4410 using A::f; // #1
4411 using B<int>::g; // #2
4412 };
4413
4414 In case #1 where `A::f' is a member template, DECL_ACCESS is
4415 recorded in the primary template but not in its specialization.
4416 We check access of FN using its primary template.
4417
4418 In case #2, where `B<int>::g' has a DECL_TEMPLATE_INFO simply
4419 because it is a member of class template B, DECL_ACCESS is
4420 recorded in the specialization `B<int>::g'. We cannot use its
4421 primary template because `B<T>::g' and `B<int>::g' may have
4422 different access. */
4423 if (DECL_TEMPLATE_INFO (fn)
4424 && is_member_template (DECL_TI_TEMPLATE (fn)))
4425 perform_or_defer_access_check (cand->access_path,
4426 DECL_TI_TEMPLATE (fn));
4427 else
4428 perform_or_defer_access_check (cand->access_path, fn);
4429 }
4430
4431 if (args && TREE_CODE (args) != TREE_LIST)
4432 args = build_tree_list (NULL_TREE, args);
4433 arg = args;
4434
4435 /* The implicit parameters to a constructor are not considered by overload
4436 resolution, and must be of the proper type. */
4437 if (DECL_CONSTRUCTOR_P (fn))
4438 {
4439 converted_args = tree_cons (NULL_TREE, TREE_VALUE (arg), converted_args);
4440 arg = TREE_CHAIN (arg);
4441 parm = TREE_CHAIN (parm);
4442 if (DECL_HAS_IN_CHARGE_PARM_P (fn))
4443 /* We should never try to call the abstract constructor. */
4444 abort ();
4445 if (DECL_HAS_VTT_PARM_P (fn))
4446 {
4447 converted_args = tree_cons
4448 (NULL_TREE, TREE_VALUE (arg), converted_args);
4449 arg = TREE_CHAIN (arg);
4450 parm = TREE_CHAIN (parm);
4451 }
4452 }
4453 /* Bypass access control for 'this' parameter. */
4454 else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
4455 {
4456 tree parmtype = TREE_VALUE (parm);
4457 tree argtype = TREE_TYPE (TREE_VALUE (arg));
4458 tree converted_arg;
4459 tree base_binfo;
4460
4461 if (ICS_BAD_FLAG (TREE_VEC_ELT (convs, i)))
4462 pedwarn ("passing `%T' as `this' argument of `%#D' discards qualifiers",
4463 TREE_TYPE (argtype), fn);
4464
4465 /* [class.mfct.nonstatic]: If a nonstatic member function of a class
4466 X is called for an object that is not of type X, or of a type
4467 derived from X, the behavior is undefined.
4468
4469 So we can assume that anything passed as 'this' is non-null, and
4470 optimize accordingly. */
4471 my_friendly_assert (TREE_CODE (parmtype) == POINTER_TYPE, 19990811);
4472 /* Convert to the base in which the function was declared. */
4473 my_friendly_assert (cand->conversion_path != NULL_TREE, 20020730);
4474 converted_arg = build_base_path (PLUS_EXPR,
4475 TREE_VALUE (arg),
4476 cand->conversion_path,
4477 1);
4478 /* Check that the base class is accessible. */
4479 if (!accessible_base_p (TREE_TYPE (argtype),
4480 BINFO_TYPE (cand->conversion_path)))
4481 error ("`%T' is not an accessible base of `%T'",
4482 BINFO_TYPE (cand->conversion_path),
4483 TREE_TYPE (argtype));
4484 /* If fn was found by a using declaration, the conversion path
4485 will be to the derived class, not the base declaring fn. We
4486 must convert from derived to base. */
4487 base_binfo = lookup_base (TREE_TYPE (TREE_TYPE (converted_arg)),
4488 TREE_TYPE (parmtype), ba_ignore, NULL);
4489 converted_arg = build_base_path (PLUS_EXPR, converted_arg,
4490 base_binfo, 1);
4491
4492 converted_args = tree_cons (NULL_TREE, converted_arg, converted_args);
4493 parm = TREE_CHAIN (parm);
4494 arg = TREE_CHAIN (arg);
4495 ++i;
4496 is_method = 1;
4497 }
上面在调用 build_over_call 之前,对于非构造函数,构建了一个与 expr 类型相同的“ this ”指针。不过,这个方法可能被定义在基类,而不是在 expr 的类型中,对于这个情形,需要从该类型获取这个基类。因此在 4475 行, arg 将是在 convert_like_real 中准备的“ this ”指针,而 build_base_path 产生 PLUS_EXPR 来调整 arg 指向由“ cand->conversion_path ”指定的基类的开头。
接着在 4487 行,正如我们已经看到的,“ TREE_TYPE (converted_arg) ”将是基类类型的 TYPE_DECL ,其 TREE_TYPE 将是对应的 RECORD_TYPE ;而 parmtype 是在方法定义中隐含的“ this ”参数,它将是真正定义这个方法的类。 在 4487 行开始,需要进一步的调整,出于由下面例子所显示的事实:
class A {
protected : int operator () { return 0; }
};
class B: public A {
public : using A::operator ;
};
class C: public B {};
void func (int) {}
int main() {
func (C c);
}
当查找转换序列时,前端将选择 B 作为“ cand->conversion_path ”;不过,该转换操作符事实上在 A 里定义。这个真正的“ this ”实参应该是派生类中的子对象 A 。注意,通过 using 指示( using directive ),基类的私有成员可以在派生类中可见,甚至于在外部的名字空间。因此 4487 行开始的调整,在 lookup_base 中检查可访问性就足够了。
5.12.5.2.2.2.1. 普通实参
在转换了隐含的“ this ”实参之后,接着转换其他实参。下面, arg 是用户所传递的实参列表,其中的节点 TREE_VALUE 是对应实参的表达式;而 parm 则是该函数的形参列表,其中的节点 TREE_VALUE 是对应的类型,而 TREE_PURPOSE 是实参缺省值,如果有的话。
build_over_call (continue)
4499 for (; arg && parm;
4500 parm = TREE_CHAIN (parm), arg = TREE_CHAIN (arg), ++i)
4501 {
4502 tree type = TREE_VALUE (parm);
4503
4504 conv = TREE_VEC_ELT (convs, i);
4505 val = convert_like_with_context
4506 (conv, TREE_VALUE (arg), fn, i - is_method);
4507
4508 val = convert_for_arg_passing (type, val);
4509 converted_args = tree_cons (NULL_TREE, val, converted_args);
4510 }
4511
4512 /* Default arguments */
4513 for (; parm && parm != void_list_node; parm = TREE_CHAIN (parm), i++)
4514 converted_args
4515 = tree_cons (NULL_TREE,
4516 convert_default_arg (TREE_VALUE (parm),
4517 TREE_PURPOSE (parm),
4518 fn, i - is_method),
4519 converted_args);
4520
4521 /* Ellipsis */
4522 for (; arg; arg = TREE_CHAIN (arg))
4523 {
4524 tree a = TREE_VALUE (arg);
4525 if (magic_varargs_p (fn))
4526 /* Do no conversions for magic varargs. */ ;
4527 else
4528 a = convert_arg_to_ellipsis (a);
4529 converted_args = tree_cons (NULL_TREE, a, converted_args);
4530 }
4531
4532 converted_args = nreverse (converted_args);
另外, convs 的 TREE_VEC_ELT 记录了对应于用户提供的实参的转换序列。连同这个转换序列,在 4508 行的 convert_like_with_context 被对应如下。
52 #define convert_like_with_context (CONV, EXPR, FN, ARGNO) / in call.c
53 convert_like_real ((CONV), (EXPR), (FN), (ARGNO), 0, /
54 /*issue_conversion_warnings=*/ true)
它为每个实参调用了 convert_like_real 。这里警告信息应该被尽快地发布,并且对于实参,它是一个顶层的调用,因而实参 inner 的值是 0 。
在经过了我们现在正在学习的普通转换后,实参需要由下面函数提供的额外转换。
4317 tree
4318 convert_for_arg_passing (tree type, tree val) in call.c
4319 {
4320 if (val == error_mark_node)
4321 ;
4322 /* Pass classes with copy ctors by invisible reference. */
4323 else if (TREE_ADDRESSABLE (type))
4324 val = build1 (ADDR_EXPR, build_reference_type (type), val);
4325 else if (PROMOTE_PROTOTYPES
4326 && INTEGRAL_TYPE_P (type)
4327 && COMPLETE_TYPE_P (type)
4328 && INT_CST_LT_UNSIGNED (TYPE_SIZE (type),
4329 TYPE_SIZE (integer_type_node)))
4330 val = perform_integral_promotions (val);
4331 return val;
4332 }
考虑以下的例子 [7] :
A a;
func (a); // of declaration void func (A a)
编译器将产生下面的伪代码来实现上述语句的语义:
A __temp0; // temporary generated by compiler
__temp0.A::A(a); // invocation of copy ctor by compiler
func (__temp0);
拷贝构造函数必须被调用来初始化临时对象,以符合拷贝构造函数的语义。如果编译器把 func 改变为“ void func (A& a) ”,效率会更高。这里有一个额外前提条件,就是“ a ”必须不能放入寄存器(对于放入寄存器这个情形, TREE_ADDRESSABLE 返回 0 )。
然后, 4325 行的 PROMOTE_PROTOTYPES ,对于 x86 芯片,被定义为 1 ,它表示当一个原型声明为‘ char ’或‘ short ’,真正传入一个‘ int ’( x86 向栈压入小于 int 的数据,相当不易)。因此在 4330 行的 perform_integral_promotions ,对这种情形,执行这个提升。