5.13.
解析后阶段
5.13.1.
预备知识—转换的细节
5.13.1.1.
确定合适的转换
C++
是一个强类型语言。不过,在实践中,我们总是可以写出这样的代码:“
1 + 5.0f;
”,其中“
1
”
是整数类型,而“
5.0f
”是一个浮点类型。我们甚至可以写出更惊人的代码,像这样:“
a + b;
”,假定“
a
”及“
b
”都是某个类的实例。这是因为编译器将为我们产生执行必要、合适转换的代码。在这一节中,我们将看到编译器在背后使用的转换,及由语言标准所制定的转换规则。
我们首先从函数
can_convert_arg
开始,该函数用于测试两个指定的类型是否匹配。
5996
bool
5997
can_convert_arg
(tree to, tree from,
tree arg)
in
call.c
5998
{
5999
tree t = implicit_conversion
(to, from, arg, LOOKUP_NORMAL);
6000
return
(t
&& ! ICS_BAD_FLAG (t));
6001
}
由其名字所显示,由下面函数执行的转换,是编译器可以自由使用的,只要它觉得合适。
它是
C++
语言一个有趣及强大的特性;而且经常给程序员,甚至老鸟带来困惑。【
3
】,章节
13.3.3
.1
“隐含转换序列”详细解释了什么是隐式转换:
1. 2. 3. — — — 4. 5. 6. 7. 8. 9. 10. 这个规则防止一个函数因为其某个形参的二义性转换序列,而成为不可行。考虑这个例子, class class class class void f(A) { } void f(C) { } B b; f(b); // // b -> A 如果没有这个规则,对于调用 如果一个使用二义性转换序列的函数被选择为最佳可行函数,该调用将是错误的,因为该调用中某个实参的转换是有二义性的。 11. |
在深入代码之前,我们首先要阐明左值(
lvalue
)及右值(
rvalue
)的概念。一个左值是指一个对象或函数。它必须是一个可取值的实体。即如果它是指对象的话,该对象应该能够被放在一个赋值操作的左手侧(这也是为什么它被称为左值)。而非左值的就被称为右值(它们不能用在赋值操作的左手侧)。
1097
static
tree
1098
implicit_conversion
(tree to, tree from, tree expr, int flags)
in call.c
1099
{
1100
tree conv;
1101
1102
if (from == error_mark_node
|| to == error_mark_node
1103
|| expr ==
error_mark_node)
1104
return
NULL_TREE;
1105
1106
if (TREE_CODE (to) ==
REFERENCE_TYPE)
1107
conv = reference_binding
(to, from, expr, flags);
1108
else
1109
conv = standard_conversion
(to, from, expr, flags);
1110
1111
if (conv)
1112
return
conv;
1113
1114
if (expr != NULL_TREE
1115
&& (IS_AGGR_TYPE
(from)
1116
|| IS_AGGR_TYPE
(to))
1117
&& (flags &
LOOKUP_NO_CONVERSION) == 0)
1118
{
1119
struct
z_candidate
*cand;
1120
1121
cand = build_user_type_conversion_1
1122
(to, expr,
LOOKUP_ONLYCONVERTING);
1123
if (cand)
1124
conv =
cand->second_conv;
1125
1126
/*
We used to try to bind a reference to a temporary here, but that
1127
now
handled by the recursive call to this function at the end
1128
of
reference_binding.
*/
1129
return
conv;
1130
}
1131
1132
return
NULL_TREE;
1133
}
看到转换处理函数可能会相互递归,在这里,为了处理引用类型,该函数再一次调用了
reference_binding
。不过,因为我们正在表达式的树中前进,只要没有回环的依赖,就不会有无限递归。因为
C++
是要求使用前必须声明的语言,相互依赖是非法的;解析器应该能够找出这个非法的形式。
5.13.1.1.1.
标准转换序列
【
3
】章节
13.3.3
.1.1
“标准转换序列”给出关于序列的细节。
1. 2. 3. 表
|
下面我们跳过
472~479
行对重载的解析(在后面有关引用绑定的章节再来看它,记得
type_unknown_p
返回
true
,如果
expr
是一个重载)。还要记住下面的
standard_conversion
处理隐式转换,它只考虑上面提到的转换;千万不要把它与强制转换混淆。
456
static
tree
457
standard_conversion
(tree to, tree from,
tree expr, int flags)
in
call.c
458
{
459
enum
tree_code fcode, tcode;
460
tree conv;
461
bool fromref = false;
462
463
to = v (to);
464
if (TREE_CODE (from) == REFERENCE_TYPE)
465
{
466
fromref = true;
467
from = TREE_TYPE (from);
468
}
469
to = strip_top_quals (to);
470
from = strip_top_quals (from);
471
472
if ((TYPE_PTRFN_P (to) || TYPE_PTRMEMFUNC_P
(to))
473
&& expr && type_unknown_p
(expr))
474
{
475
expr = instantiate_type (to, expr,
tf_conv);
476
if (expr == error_mark_node)
477
return
NULL_TREE;
478
from = TREE_TYPE (expr);
479
}
480
481
fcode = TREE_CODE (from);
482
tcode = TREE_CODE (to);
483
484
conv = build1
(IDENTITY_CONV, from, expr);
485
486
if (fcode == FUNCTION_TYPE)
487
{
488
from = build_pointer_type
(from);
489
fcode = TREE_CODE (from);
490
conv = build_conv
(LVALUE_CONV, from, conv);
491
}
492
else if (fcode == ARRAY_TYPE)
493
{
494
from = build_pointer_type
(TREE_TYPE (from));
495
fcode = TREE_CODE (from);
496
conv = build_conv
(LVALUE_CONV, from, conv);
497
}
498
else if (fromref || (expr
&& lvalue_p
(expr)))
499
conv = build_conv
(RVALUE_CONV, from, conv);
500
501
/* Allow conversion
between `__complex__' data types.
*/
502
if (tcode == COMPLEX_TYPE && fcode ==
COMPLEX_TYPE)
503
{
504
/* The standard
conversion sequence to convert FROM to TO is
505
the standard
conversion sequence to perform componentwise
506
conversion.
*/
507
tree part_conv = standard_conversion
508
(TREE_TYPE (to), TREE_TYPE (from),
NULL_TREE, flags);
509
510
if (part_conv)
511
{
512
conv = build_conv
(TREE_CODE (part_conv), to, conv);
513
ICS_STD_RANK (conv) = ICS_STD_RANK
(part_conv);
514
}
515
else
516
conv = NULL_TREE;
517
518
return
conv;
519
}
520
521
if (same_type_p (from, to))
522
return
conv;
484
行为恒等转换构建了
IDENTITY_COV
。
*_CONV
节点的
type
表示转换的源类型,而它的第一个操作数是需要转换的表达式。因此转换序列将是一个大的嵌套的
*_CONV
节点,最上层的
*_CONV
是最后执行的,而最里层的是首先执行且永远是
IDENTITY_CONV
。
在前端的内部为转换定义了如下的等级,而不是如上面表
9
所定义的那些。注意到值越小表示等级越优先。
343
#define
IDENTITY_RANK
0
in
call.c
344
#define
EXACT_RANK
1
345
#define
PROMO_RANK
2
346
#define
STD_RANK
3
347
#define
PBOOL_RANK
4
348
#define
USER_RANK
5
349
#define
ELLIPSIS_RANK
6
350
#define
BAD_RANK
7
在内部可以产生的转换有:
IDENTITY_CONV
,
LVALUE_CONV
,
QUAL_CONV
,
STD_CONV
,
PTR_CONV
,
PMEM_CONV
,
BASE_CONV
,
REF_BIND
,
USER_CONV
,
AMBIG_CONV
,及
RVALUE_CONV
。
408
static
tree
409
build_conv
(enum
tree_code code, tree type, tree from)
in
call.c
410
{
411
tree t;
412
int rank = ICS_STD_RANK (from);
413
414
/* We can't use
buildl1 here because CODE could be USER_CONV, which
415
takes two
arguments. In that case, the caller is responsible for
416
filling in the
second argument.
*/
417
t
=
make_node
(code);