在这一节中,我们要深入一些来看一下如何把实参匹配形参,并执行必须的转换。
在 C++ 前端中,函数形参被记录在 FUNCTION_TYPE 的 TYPE_ARG_TYPES 域中,它是一个 tree_list 类型的串,其节点中的 TREE_VALUE 是对应形参的类型; TREE_PURPOSE 是缺省实参值的一个表达式,如果有的话。如果在该链表中最后的节点是 void_list_node (一个 TREE_LIST 节点,其中的 TREE_VALUE 是 void_type_node ),那么这个类型的函数不接受可变数目参数。否则,它接受可变数目参数。并且该函数的实参将被构建成一个 tree_list 链表,其节点中的 TREE_VALUE 是一个赋值表达式的一个表示。
在下面的函数中,参数 typelist 是用于形参的 tree_list ,而 values 是用于实参的 tree_list 。因此在不使用可变数目参数的情形下( typelist 以 void_list_node 结尾), typelist 与 values 的长度相同(这时,若有缺省实参,它被加 values 中)。 2573 行的条件过滤出错误的情形,因为我们以相同的速率加入 typelist 及 values 。
确定转换序列及转换的真正执行的细节,由 预备知识—转换的细节 一节给出。
2538 tree
2539 convert_arguments (tree typelist, tree values, tree fndecl, int flags) in typeck.c
2540 {
2541 tree typetail, valtail;
2542 tree result = NULL_TREE;
2543 const char *called_thing = 0;
2544 int i = 0;
2545
2546 /* Argument passing is always copy-initialization. */
2547 flags |= LOOKUP_ONLYCONVERTING;
2548
2549 if (fndecl)
2550 {
2551 if (TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
2552 {
2553 if (DECL_NAME (fndecl) == NULL_TREE
2554 || IDENTIFIER_HAS_TYPE_VALUE (DECL_NAME (fndecl)))
2555 called_thing = "constructor";
2556 else
2557 called_thing = "member function";
2558 }
2559 else
2560 called_thing = "function";
2561 }
2562
2563 for (valtail = values, typetail = typelist;
2564 valtail;
2565 valtail = TREE_CHAIN (valtail), i++)
2566 {
2567 tree type = typetail ? TREE_VALUE (typetail) : 0;
2568 tree val = TREE_VALUE (valtail);
2569
2570 if (val == error_mark_node)
2571 return error_mark_node;
2572
2573 if (type == void_type_node)
2574 {
2575 if (fndecl)
2576 {
2577 cp_error_at ("too many arguments to %s `%+#D'", called_thing,
2578 fndecl);
2579 error ("at this point in file");
2580 }
2581 else
2582 error ("too many arguments to function");
2583 /* In case anybody wants to know if this argument
2584 list is valid. */
2585 if (result)
2586 TREE_TYPE (tree_last (result)) = error_mark_node;
2587 break ;
2588 }
2589
2590 /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
2591 Strip such NOP_EXPRs, since VAL is used in non-lvalue context. */
2592 if (TREE_CODE (val) == NOP_EXPR
2593 && TREE_TYPE (val) == TREE_TYPE (TREE_OPERAND (val, 0))
2594 && (type == 0 || TREE_CODE (type) != REFERENCE_TYPE))
2595 val = TREE_OPERAND (val, 0);
2596
2597 if (type == 0 || TREE_CODE (type) != REFERENCE_TYPE)
2598 {
2599 if (TREE_CODE (TREE_TYPE (val)) == ARRAY_TYPE
2600 || TREE_CODE (TREE_TYPE (val)) == FUNCTION_TYPE
2601 || TREE_CODE (TREE_TYPE (val)) == METHOD_TYPE)
2602 val = decay_conversion (val);
2603 }
2604
2605 if (val == error_mark_node)
2606 return error_mark_node;
2607
2608 if (type != 0)
2609 {
2610 /* Formal parm type is specified by a function prototype. */
2611 tree parmval;
2612
2613 if (!COMPLETE_TYPE_P (complete_type (type)))
2614 {
2615 if (fndecl)
2616 error ("parameter %P of `%D' has incomplete type `%T'",
2617 i, fndecl, type);
2618 else
2619 error ("parameter %P has incomplete type `%T'", i, type);
2620 parmval = error_mark_node;
2621 }
2622 else
2623 {
2624 parmval = convert_for_initialization
2625 (NULL_TREE, type, val, flags,
2626 "argument passing", fndecl, i);
2627 parmval = convert_for_arg_passing (type, parmval);
2628 }
2629
2630 if (parmval == error_mark_node)
2631 return error_mark_node;
2632
2633 result = tree_cons (NULL_TREE, parmval, result);
2634 }
2635 else
2636 {
2637 if (TREE_CODE (TREE_TYPE (val)) == REFERENCE_TYPE)
2638 val = convert_from_reference (val);
2639
2640 if (fndecl && DECL_BUILT_IN (fndecl)
2641 && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CONSTANT_P)
2642 /* Don't do ellipsis conversion for __built_in_constant_p
2643 as this will result in spurious warnings for non-POD
2644 types. */
2645 val = require_complete_type (val);
2646 else
2647 val = convert_arg_to_ellipsis (val);
2648
2649 result = tree_cons (NULL_TREE, val, result);
2650 }
2651
2652 if (typetail)
2653 typetail = TREE_CHAIN (typetail);
2654 }
如果 type 是在函数原型中指定的类型,如 2608 行所示,需要确保对应的实参能被转换到该形参的类型,然后执行这个转换(看到处理传入的实参非常类似于把它放在赋值表达式的右手侧)。在这之后, convert_for_arg_passing 为传入的实参,基于目标平台,提供了特定的转换。这里是 convert_for_initialization 的细节。
5809 tree
5810 convert_for_initialization (tree exp, tree type, tree rhs, int flags, in typeck.c
5811 const char *errtype, tree fndecl, int parmnum)
5812 {
5813 enum tree_code codel = TREE_CODE (type);
5814 tree rhstype;
5815 enum tree_code coder;
5816
5817 /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
5818 Strip such NOP_EXPRs, since RHS is used in non-lvalue context. */
5819 if (TREE_CODE (rhs) == NOP_EXPR
5820 && TREE_TYPE (rhs) == TREE_TYPE (TREE_OPERAND (rhs, 0))
5821 && codel != REFERENCE_TYPE)
5822 rhs = TREE_OPERAND (rhs, 0);
5823
5824 if (rhs == error_mark_node
5825 || (TREE_CODE (rhs) == TREE_LIST && TREE_VALUE (rhs) == error_mark_node))
5826 return error_mark_node;
5827
5828 if (TREE_CODE (TREE_TYPE (rhs)) == REFERENCE_TYPE)
5829 rhs = convert_from_reference (rhs);
5830
5831 if ((TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE
5832 && TREE_CODE (type) != ARRAY_TYPE
5833 && (TREE_CODE (type) != REFERENCE_TYPE
5834 || TREE_CODE (TREE_TYPE (type)) != ARRAY_TYPE))
5835 || (TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE
5836 && (TREE_CODE (type) != REFERENCE_TYPE
5837 || TREE_CODE (TREE_TYPE (type)) != FUNCTION_TYPE))
5838 || TREE_CODE (TREE_TYPE (rhs)) == METHOD_TYPE)
5839 rhs = decay_conversion (rhs);
5840
5841 rhstype = TREE_TYPE (rhs);
5842 coder = TREE_CODE (rhstype);
5843
5844 if (coder == ERROR_MARK)
5845 return error_mark_node;
5846
5847 /* We accept references to incomplete types, so we can
5848 return here before checking if RHS is of complete type. */
5849
5850 if (codel == REFERENCE_TYPE)
5851 {
5852 /* This should eventually happen in convert_arguments. */
5853 int savew = 0, savee = 0;
5854
5855 if (fndecl)
5856 savew = warningcount , savee = errorcount ;
5857 rhs = initialize_reference (type, rhs, /*decl=*/ NULL_TREE,
5858 /*cleanup=*/ NULL);
5859 if (fndecl)
5860 {
5861 if (warningcount > savew)
5862 cp_warning_at ("in passing argument %P of `%+D'", parmnum, fndecl);
5863 else if (errorcount > savee)
5864 cp_error_at ("in passing argument %P of `%+D'", parmnum, fndecl);
5865 }
5866 return rhs;
5867 }
5868
5869 if (exp != 0)
5870 exp = require_complete_type (exp);
5871 if (exp == error_mark_node)
5872 return error_mark_node;
5873
5874 rhstype = non_reference (rhstype);
5875
5876 type = complete_type (type);
5877
5878 if (IS_AGGR_TYPE (type))
5879 return ocp_convert (type, rhs, CONV_IMPLICIT|CONV_FORCE_TEMP, flags);
5880
5881 return convert_for_assignment (type, rhs, errtype, fndecl, parmnum);
5882 }
首先,传入的具有引用类型的实参( rhs )(但对应形参不是)应该拷贝其值,因为该引用应该被忽略,因此需要为这个目的构建一个 INDIRECT_REF 表达式。 5831 行的条件,表示, 1 )我们向一个既不是数组类型或数组引用类型的形参传入一个数组; 2 )传入一个函数(事实上,其地址),但对应的形参即不是函数类型,也不是函数的引用类型;或 3 )传入一个方法(事实上,其地址,并注意到方法不能被引用因为它要求隐含的‘ this ’指针实参);那么把 rhs 根据左值 - 到 - 右值转换衰减。
接着如果形参具有引用类型,传入的实参将被引用而不是被值拷贝。它通过 initialize_reference 以不同于值拷贝的方式处理。在 引用类型的转换 一节中,显示了通过 reference_binding 确定实现从参数到形参的引用类型的转换序列的过程。 reference_binding 将返回 NULL ,如果不存在这样的转换序列;或“ ICS_BAD_FLAG (conv) ”成立,如果该转换不被允许。
6120 tree
6121 initialize_reference (tree type, tree expr, tree decl, tree *cleanup) in call.c
6122 {
6123 tree conv;
6124
6125 if (type == error_mark_node || error_operand_p (expr))
6126 return error_mark_node;
6127
6128 conv = reference_binding (type, TREE_TYPE (expr), expr, LOOKUP_NORMAL);
6129 if (!conv || ICS_BAD_FLAG (conv))
6130 {
6131 if (!(TYPE_QUALS (TREE_TYPE (type)) & TYPE_QUAL_CONST)
6132 && !real_lvalue_p (expr))
6133 error ("invalid initialization of non-const reference of "
6134 "type '%T' from a temporary of type '%T'",
6135 type, TREE_TYPE (expr));
6136 else
6137 error ("invalid initialization of reference of type "
6138 "'%T' from expression of type '%T'", type,
6139 TREE_TYPE (expr));
6140 return error_mark_node;
6141 }
… // …
6177 my_friendly_assert (TREE_CODE (conv) == REF_BIND, 20030302);
6178 if (decl)
6179 {
…
6268 return build_nop (type, expr);
6269 }
6270
6271 /* Perform the conversion. */
6272 return convert_like (conv, expr);
6273 }
上面如果实参 decl 不是 NULL ,它就是由 expr 初始化的 VAR_DECL 。在我们这里的场景中,因为我们正在处理调用实参, decl 是 NULL ,这需要编译器“人工”地构建临时对象。而 6272 行的 convert_like 只是 convert_like_real 的一个简单的封装;可以直接调用这个函数执行这个转换,因为对于引用的形参,我们不需要操心实参的生命周期。
而如果形参不是引用类型,实参应该被拷贝。作为先决条件,对于形参及实参的类型定义必须已经完成。进一步注意到 rhstype 给出了实参的类型,如果我们把一个引用用作实参,引用语义应该被从该类型中丢弃(看到上面我们构建 INDIRECT_REF 来使用实参的值),在这里 non_reference 获取了被援引的类型。
接着如果形参是一个类类型,需要调用相应的构造函数来从实参构建实例,这是上面 ocp_convert 的任务。注意到其实参 flags 是 LOOKUP_NORMAL (比特集 LOOKUP_PROTECT | LOOKUP_COMPLAIN ),它强制该函数查找匹配的构造函数,但跳过转换操作符;而实参 convtype 则是比特集 CONV_IMPLICIT|CONV_FORCE_TEMP ,它表示查找隐式转换并需要 一个临时对象来保存结果。
对于其他非引用类型的函数形参,它遵循与赋值相同的规则,即该函数的实参可以被视作一个表达式右手边部分。
5712 static tree
5713 convert_for_assignment (tree type, tree rhs, in typeck.c
5714 const char *errtype, tree fndecl, int parmnum)
5715 {
5716 tree rhstype;
5717 enum tree_code coder;
5718
5719 /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
5720 if (TREE_CODE (rhs) == NON_LVALUE_EXPR)
5721 rhs = TREE_OPERAND (rhs, 0);
5722
5723 rhstype = TREE_TYPE (rhs);
5724 coder = TREE_CODE (rhstype);
5725
5726 if (TREE_CODE (type) == VECTOR_TYPE && coder == VECTOR_TYPE
5727 && ((*targetm .vector_opaque_p) (type)
5728 || (*targetm .vector_opaque_p) (rhstype)))
5729 return convert (type, rhs);
5730
5731 if (rhs == error_mark_node || rhstype == error_mark_node)
5732 return error_mark_node;
5733 if (TREE_CODE (rhs) == TREE_LIST && TREE_VALUE (rhs) == error_mark_node)
5734 return error_mark_node;
5735
5736 /* The RHS of an assignment cannot have void type. */
5737 if (coder == VOID_TYPE)
5738 {
5739 error ("void value not ignored as it ought to be");
5740 return error_mark_node;
5741 }
5742
5743 /* Simplify the RHS if possible. */
5744 if (TREE_CODE (rhs) == CONST_DECL)
5745 rhs = DECL_INITIAL (rhs);
5746
5747 /* We do not use decl_constant_value here because of this case:
5748
5749 const char* const s = "s";
5750
5751 The conversion rules for a string literal are more lax than for a
5752 variable; in particular, a string literal can be converted to a
5753 "char *" but the variable "s" cannot be converted in the same
5754 way. If the conversion is allowed, the optimization should be
5755 performed while creating the converted expression. */
5756
5757 /* [expr.ass]
5758
5759 The expression is implicitly converted (clause _conv_) to the
5760 cv-unqualified type of the left operand.
5761
5762 We allow bad conversions here because by the time we get to this point
5763 we are committed to doing the conversion. If we end up doing a bad
5764 conversion, convert_like will complain. */
5765 if (!can_convert_arg_bad (type, rhstype, rhs))
5766 {
5767 /* When -Wno-pmf-conversions is use, we just silently allow
5768 conversions from pointers-to-members to plain pointers. If
5769 the conversion doesn't work, cp_convert will complain. */
5770 if (!warn_pmf2ptr
5771 && TYPE_PTR_P (type)
5772 && TYPE_PTRMEMFUNC_P (rhstype))
5773 rhs = cp_convert (strip_top_quals (type), rhs);
5774 else
5775 {
5776 /* If the right-hand side has unknown type, then it is an
5777 overloaded function. Call instantiate_type to get error
5778 messages. */
5779 if (rhstype == unknown_type_node)
5780 instantiate_type (type, rhs, tf_error | tf_warning);
5781 else if (fndecl)
5782 error ("cannot convert `%T' to `%T' for argument `%P' to `%D'",
5783 rhstype, type, parmnum, fndecl);
5784 else
5785 error ("cannot convert `%T' to `%T' in %s", rhstype, type,
5786 errtype);
5787 return error_mark_node;
5788 }
5789 }
5790 return perform_implicit_conversion (strip_top_quals (type), rhs);
5791 }
上面的注释解释了在赋值中对转换的特殊处理。把实参转换到形参类型也适用隐式转换。下面的函数,类似于 can_convert_arg 查找转换序列,但允许可疑的转换。
6005 bool
6006 can_convert_arg_bad (tree to, tree from, tree arg) in call.c
6007 {
6008 return implicit_conversion (to, from, arg, LOOKUP_NORMAL) != 0;
6009 }
在这里这个形参不是引用类型,那么被转换的实参,在其后的使用之前,应该先拷贝其值,因此这需要把实参转换到形参没有最顶端的 cv- 限定的类型。余下的处理与其他转换的方式相同。
回到 convert_arguments ,对于 2635 行的省略实参,对于这里非引用类型的形参,而引用类型的实参,该实参值就是我们所感兴趣的。在 2641 行,代码 BUILT_IN_CONSTANT_P 对应于内建函数“ constant_p ”,该函数接受可变实参,并显示其实参是否是常量。而看到在 2647 行的 convert_arg_to_ellipsis ,当该实参不是 POD 类型(这个类型可以被传给 constant_p ,这个函数仅评估其常量性而不关心其他事)时,给出警告。因此就不再需要费力调用 convert_arg_to_ellipsis 。
接着下面的代码处理缺省实参的情形,它与在隐式转换中查找用户定义转换的过程相同。
convert_arguments (continue)
2656 if (typetail != 0 && typetail != void_list_node)
2657 {
2658 /* See if there are default arguments that can be used. */
2659 if (TREE_PURPOSE (typetail)
2660 && TREE_CODE (TREE_PURPOSE (typetail)) != DEFAULT_ARG)
2661 {
2662 for (; typetail != void_list_node; ++i)
2663 {
2664 tree parmval
2665 = convert_default_arg (TREE_VALUE (typetail),
2666 TREE_PURPOSE (typetail),
2667 fndecl, i);
2668
2669 if (parmval == error_mark_node)
2670 return error_mark_node;
2671
2672 result = tree_cons (0, parmval, result);
2673 typetail = TREE_CHAIN (typetail);
2674 /* ends with `...'. */
2675 if (typetail == NULL_TREE)
2676 break ;
2677 }
2678 }
2679 else
2680 {
2681 if (fndecl)
2682 {
2683 cp_error_at ("too few arguments to %s `%+#D'",
2684 called_thing, fndecl);
2685 error ("at this point in file");
2686 }
2687 else
2688 error ("too few arguments to function");
2689 return error_mark_list ;
2690 }
2691 }
2692
2693 return nreverse (result);
2694 }