您的位置:首页 > 编程语言

GCC-3.4.6源代码学习笔记(145)

2011-01-15 11:24 459 查看

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.

一个隐式转换序列是,把一个函数调用中的一个实参转换到对应形参的类型的,转换序列。如条文
4
所定义,这个转换序列是一个隐式转换,这意味着它遵守对象初始化,及单个表达式引用(
reference by a single expression
)的规则(
8.5

8.5.3
)。

2.
隐式转换序列只关心实参的类型,
cv-
限定(
cv-qualification
),及左值性(
lvalueness
);以及如何转换这些来匹配形参的对应属性。其他属性,例如实参的生命周期,存储类别,对齐,或访问性,及实参是否是一个位域(
bit-field
),都被忽略。因而,虽然对于一个指定的形参
-
实参对可以定义出一个隐式转换序列,在最后的分析中,该从实参到形参的转换仍可能被发现是错误的。

3.

一个良好的隐式转换序列是以下形式之一:


一个标准转换序列(
13.3.3
.1.1
),


一个用户定义转换序列(
13.3.3
.1.2
),或者


一个省略(
ellipsis
)转换序列(
13.3.3
.1.3
)。

4.

不过,当考虑由
13.3.1
.3
选定的用于一个类拷贝初始化第二步中临时对象拷贝,或由
13.3.1.4

13.3.1.5
,或
13.3.1.6
中所有情况下选定的,一个用户定义转换函数的实参时,只允许标准转换序列及省略转换序列。

5.

在形参类型是一个引用的情况下,参见
13.3.3
.1.4.
(引用绑定
reference binding




6.

当形参类型不是一个引用时,隐式转换序列从实参表达式塑造出形参的一个拷贝初始化。该隐式转换序列被要求把实参表达式转换到形参类型的一个右值。
[
注意:当该形参是一个类类型,这是一个以条文
13
为目的的概念上的转换(
this
is a conceptual conversion defined for the purposes of clause 13
);事实上的初始化依据构造函数来定义,而不是一个转换
]
。任何最上层的
cv-
限定上的差异,被初始化本身所包括,不会构成一个转换。
[
例如:一个具有类型
A
的形参可以被一个具有类型
const A
的实参初始化。这个情形下的隐式转换序列是恒等序列(
the
identity sequence
);从
const A

A
,不包含“转换”
]
。当形参具有一个类类型,并且实参表达式具有相同的类型,该隐式转换序列是一个恒等转换(
identity conversion
)。当形参具有一个类类型,而实参表达式具有一个派生类类型,隐式转换序列是一个派生类到基类的
derived-to-base
转换。
[
注意:没有这样的标准转换;这个
derived-to-base
转换仅存在于隐式转换序列的描述中
]
。一个
derived-to-base
转换具有转换等级(
Conversion rank
)(
13.3.3
.1.1
)。

7.

在所有的上下文中,当转换到隐含对象形参,或转换到一个赋值操作的左侧操作数时,
仅允许不构建用于结果的临时对象的标准转换序列。

8.

如果匹配一个实参到一个形参类型不要求转换,这个隐式转换序列是包含恒等转换的标准转换序列(
13.3.3
.1.1
)。

9.

如果不能找到一个转换序列来转换一个实参到形参的类型,或者该转换是错误的,则不能形成隐式转换序列。

10.
如果存在多个不同的转换序列把实参转换到形参的类型,与该形参关联的隐式转换序列被定义为显示出二义性转换序列的唯一的转换序列(
the unique conversion sequence designated the ambiguous conversion
sequence
)。出于以
13.3.3
.2
中所描述的隐式转换排名的目的,二义性转换序列被作为用户定义序列来处理,即与其他用户定义转换序列无法区分(二义性转换序列被安排做与用户定义转换序列等级相同,是因为对于一个实参,仅当涉及不同的用户定义转换时,才可能存在多个转换序列。二义性转换序列与其他用户转换序列不可区分,在于它至少代表了
2
个用户定义转换序列,每个具有不同的用户定义转换,而其他用户定义转换序列必然与其中至少一个不能区分。

这个规则防止一个函数因为其某个形参的二义性转换序列,而成为不可行。考虑这个例子,

class
B;

class
A { A (B&);
};

class
B { operator
A (); };

class
C { C (B&);
};

void f(A) { }

void f(C) { }

B b;

f(b); //

二义性的,因为
b -> C
通过构造函数,而

// b -> A
通过构造函数或转换函数

如果没有这个规则,对于调用
f(b)

f(A)
将被取消作为可行函数,从而导致重载解析选择
f(C)
作为实际的调用,尽管显然地,它不是最佳选择。另一方面,如果声明了一个
f(B)
,那么
f(b)
将被解析为
f(B)
,因为与
f(B)
的完全匹配优于匹配
f(A)
所要求的序列。

如果一个使用二义性转换序列的函数被选择为最佳可行函数,该调用将是错误的,因为该调用中某个实参的转换是有二义性的。

11.
上面提及的隐式转换序列的
3
种形式定义在以下子条文中。

在深入代码之前,我们首先要阐明左值(
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.


9
总结了在条文
4
中定义的转换(
“标准转换”,【
3
】的章节
4
)并划分为
4
个不相交的类别:左值转型(
Lvalue
Transformation
),限定调整(
Qualification Adjustment
),提升(
Promotion
),以及转换(
Conversion
)。
[
注意:这些分类就左值性,
cv-
限定性,及数据表达方面而言,是正交的:左值转型不会改变类型的
cv-
限定性及数据表达;限定调整不会改变类型的左值性及数据表达;而提升及转换不会改变类型的左值性及
cv-
限定性。
]

2.
[
注意:

如条文
4
所描述,应该标准转换序列要么本身是恒等转换(即,没有转换),要么包含其他属于这
4
个类别的
1

3
个转换(
consists of
one to three conversions from the other four categories
)。在单个标准转换序列中,每个类别最多允许一个转换。如果在该序列中有
2
个或以上的转换,这些转换以规范的次序应用:左值转型
提升
转换
限定调整

—end note
]

3.

在表
9
中的每个转换都具有关联的等级(精确匹配,提升,或转换)。这些都用于评价标准转换序列(
13.3.3
.2
)。一个转换序列的等级通过考察序列中的每个转换的等级,及任意引用绑定的等级(
rank of any
reference binding
)(
13.3.3.1.4
)来决定。如果任意一个具有转换等级,该序列就是转换等级;否则,如果任意一个具有提升等级,该序列就是提升等级;否则,该序列就是精确匹配等级。


9

-
转换

转换


类别


等级


子条文


不要求转换

恒等

精确匹配

左值到右值转换

左值转型

4.1

数组到指针转换

4.2

函数到指针转换

4.3

限定转换

限定调整

4.4

整型提升

提升

提升

4.5

浮点型提升

4.6

整型转换

转换

转换

4.7

浮点型转换

4.8

浮点型到整型转换

4.9

指针转换

4.10

指针到成员转换

4.11

布尔型转换

4.12

下面我们跳过
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);

418

TREE_TYPE (t) = type;

419

TREE_OPERAND (t, 0) = from;

420

421

switch
(code)

422

{

423

case
PTR_CONV:

424

case
PMEM_CONV:

425

case
BASE_CONV:

426

case
STD_CONV:

427

if
(rank < STD_RANK)

428

rank = STD_RANK;

429

break
;

430

431

case
QUAL_CONV:

432

if (rank < EXACT_RANK)

433

rank = EXACT_RANK;

434

435

default
:

436

break
;

437

}

438

ICS_STD_RANK (t) = rank;

439

ICS_USER_FLAG (t) = (code == USER_CONV || ICS_USER_FLAG (from));

440

ICS_BAD_FLAG (t) = ICS_BAD_FLAG (from);

441

return
t;

442

}

在上面的函数里,
ICS_STD_RANK
将保存所见到的最大的等级值,在后面的章节我们将看到它的使用。

而在上面
standard_conversion


486

492
行处理函数到指针与数组到指针的转换,它们在【
3
】条文
4.2
“数组到指针转换”及条文
4.3
“函数到指针转换”中描述如下:

1.

具有类型“
array ofN T
”或“
array of unknown bound of T
”的一个左值或右值可以被转换到一个类型为“
pointer to T
”的右值。其结果是指向数组第一个元素的指针。

2.

一个非宽字符的字符串(
2.13.4
)可以被转换到一个类型为“
pointer to char
”的右值;一个宽字符的字符串可以被转换到一个类型为“
pointer to wchar_t
”的右值。在这两种情况中,其结果是指向数组第一个元素的指针。仅当目标类型是一个明确恰当的指针时,才考虑该转换;而当仅是一般的从左值到右值的转换时,不考虑该转换。
[
注意:不推荐使用这个转换。参见附录
D

]
出于在重载解析(
13.3.3.1.1
)中评估的目的,这个转换被视为一个跟有一个限定转换(
4.4
)的数组到指针的转换。
[
例如:作为一个数组到指针转换,
"abc"
被转换到“
pointer to const char
”;然后作为一个限定转换,转换到“
pointer to char
”。
]

1.

函数类型
T
的一个左值可以被转换到类型“
pointer to T
”的一个右值。其结果是指向该函数的指针。(这个转换不能应用于非静态成员函数,因为一个非静态成员函数的左值是无法得到的。)

2.
[
注意:参见
13.4
,用于重载函数的补充规则。
]

注意两个条文的条款
1
,类型“
pointer to
T
”的右值表示该指针本身是不能修改的,但其内容可以改变。例如:

char a[10];

char *p = …;

a = p;
// a is
converted to rvalue of char*, assignment not allowed

*(a+1) = *p;
// object pointed
by a+1 is lvalue, it is OK

与下面的条文相比,这
2
个条文包含了指针类型的构建,前端必须能从其他左值转型中区分出这两种情况。因此前端为它们使用了
LVALUE_CONV


然后在
standard_conversion

中,如果满足在
498
行条件,需要实现【
3
】条文
4.1
“左值到右值转换”的条款
1
。这一次前端使用
RVALUE_CONV

LVALUE_CONV

RVALUE_CONV
的一个区别在于当构建
LVALUE_CONV
时,我们已经知道这个右值了(在
488

494
行构建的指针类型),而对于
RVALUE_CONV
,在这一点上,这个右值是未知的,在后面前端需要调用
decay_conversion

来执行真正的转换。

1.

一个非函数,非数组类型的左值(
3.10
)可以被转换到一个右值。如果
T
是一个未完成的类型,必须这个转换的程序就是错误的。如果该左值引用的对象不是一个类型
T
或从
T
派生类型的对象,或者该对象未初始化,必须这个转换的程序具有不确定的行为。如果
T
是非类类型,该右值的类型是
T
的非
cv
限定的版本。否则,该右值的类型是
T

[

C++
类中,右值可以具有
cv-
限定的类型(因为它们都是对象)。这不同于
ISO C
,在
ISO C
里,非左值不能是
cv-
限定的类型。
]

2.

由左值表示的对象的值是右值的结果(
The value contained in the object indicated by the lvalue

is the rvalue

result
)。当在
sizeof

5.3.3
)的操作数中发生一个左值到右值的转换,包含在被引用对象中的值是不可访问的,因为这个操作符不会对其操作数求值。

下面的函数,给定表达式
ref

,它可以判断这个表达式是否为左值。看到上面为指针类型构建的
LVALUE_CONV
,该函数判定为右值。这个函数应该被仔细研究。

211

int

212

lvalue_p

(tree ref)
in
tree.c

213

{

214

return

215

(lvalue_p_1
(ref,
/*class rvalue ok*/
1) != clk_none);

216

}

而我们要区分的左值的类型如下。

2964

typedef
enum
cp_lvalue_kind {
in
cp-tree.h

2965

clk_none = 0,
/* Things that
are not an lvalue.
*/

2966

clk_ordinary = 1, /* An ordinary lvalue.

*/

2967

clk_class = 2,
/* An rvalue of
class-type.
*/

2968

clk_bitfield = 4, /* An lvalue for a bit-field.
*/

2969

clk_packed = 8
/* An lvalue for
a packed field.
*/

2970

} cp_lvalue_kind
;

在【
3
】,条文
3.10
(左值及右值)给出如下解释。

1.

任一表达式要么是左值,要么是右值。

2.

左值指一个对象或函数。一些右值表达式——它们具有类或
cv-
限定的类类型——也指向对象(调用构造函数或返回一个引用对象的类类型的函数的表达式,而且在这样的对象上可以调用成员函数,但这些表达式不是左值)。

3.
[
注意:某些内建操作符及函数调用会产生左值。
[
例如:如果
E
是一个指针类型的表达式,那么
*E
是一个左值的表达式,引用
E
所指向的对象或函数。另一个例子,函数

int&
f();

产生一个左值,因此调用
f()
是一个左值表达式。
]]

4.
[
注意:某些内建操作符期望左值的操作数。
[
例如:内建的赋值操作符都期望其左侧操作数是左值
]
。其他内建操作符产生右值,而一些则期望右值。
[
例如:一元及二元操作符
+
期望右值的参数,并产生右值的结果
]
。在条文
5
对每个内建操作符的讨论说明了它是否期望左值操作数,及它是否产生左值结果
]


5.

调用一个不返回引用的函数的结果是一个右值。用户定义操作符是函数,这些操作符是否期望或产生左值,由其参数及返回类型决定。

6.

保存一个由到非引用类型的转换(
cast
)引致的临时对象的表达式是一个右值(这包括使用函数记号(即,显式类型转换,
5.2.3
)所显式创建的对象。

7.

在任何时候,一个左值出现在一个期望右值的上下文中,该左值被转换为一个右值;参见
4.1

4.2
,及
4.3


8.


8.5.3
对引用初始化,及在
12.2
对临时对象的讨论,显示了在其他重要的上下文中左值及右值的行为。

9.

类右值可以有
cv-
限定的类型;非类右值总是非
cv-
限定的类型。右值应该总是具有完成的类型或
void
类型;除了这些类型。左值还可以具有未完成的类型。

10.
为了修改一个对象,该对象的一个左值是必要的;除了类类型的右值,在特定的环境下,可以用于修改其所指对象。
[
例如:对一个对象调用成员函数(
9.3
)可以修改这个对象。
]

11.
函数不可修改,但函数的指针是可修改的。

12.
一个未完成类型的指针是可修改的。在程序中的某一点,当所指向的类型完成,该指针所指向的对象亦可修改。

13.
一个
const-
限定的表达式的指称对象不应该被修改(通过这个表达式),除非它具有类类型,并且有一个可变(
mutable
)成分,该成分可以被修改(
7.1.5.1
)。

14.
如果一个表达式可以被用于修改它所引用的对象,该表达式被称为可修改。一个尝试通过一个不可修改的左值或右值表达式来修改对象的程序是不合法的。

15.
如果一个程序尝试,通过以下类型以外的一个左值,来访问一个对象的存储值,其行为是未不可预测的(这个列表的意图是指出那些一个对象可能或不可能被别名(
alias
)的环境:


该对象的动态类型(
dynamic type
),


该对象的动态类型的一个
cv-
限定的版本,


该对象的动态类型所对应的有符号或无符号类型,


该对象的动态类型的一个
cv-
限定版本所对应的有符号或无符号类型,


一个聚合或
union
类型,在其成员中包含上述类型之一(包括,递归地,一个子聚合或被包含的(
contained

union
的一个成员),


该对象的动态类型的基类类型(可能有
cv-
限定),


字符或无符号字符类型。

考虑下面对非静态方法的处理,因为非静态方法必须这样调用:“
a.fa();
”,其中给定的对象可能是不可取址的(例如,在语句“
A().fa();
”中,“
A()
”返回了一个右值),非静态方法被认为是右值。另外,表达式“
A().fa();
”可以修改这个对象,因此如果不要求严格的左值(参数
treat_class_rvalues_as_lvalues


1
),“
A()
”可被视为左值。注意
156
行的
TARGET_EXPR
,它将封装刚才提及的“
A()
”返回的临时对象(注意
161
行的注释)。

62

static
cp_lvalue_kind

63

lvalue_p_1

(tree ref,
in
tree.c

64

int treat_class_rvalues_as_lvalues)

65

{

66

cp_lvalue_kind op1_lvalue_kind = clk_none;

67

cp_lvalue_kind op2_lvalue_kind = clk_none;

68

69

if (TREE_CODE (TREE_TYPE (ref)) ==
REFERENCE_TYPE)

70

return
clk_ordinary;

71

72

if (ref == current_class_ptr

)

73

return
clk_none;

74

75

switch
(TREE_CODE (ref))

76

{

77

/* preincrements and predecrements are valid
lvals, provided

78

what they refer
to are valid lvals.
*/

79

case
PREINCREMENT_EXPR:

80

case
PREDECREMENT_EXPR:

81

case
SAVE_EXPR:

82

case
UNSAVE_EXPR:

83

case
TRY_CATCH_EXPR:

84

case
WITH_CLEANUP_EXPR:

85

case
REALPART_EXPR:

86

case
IMAGPART_EXPR:

87

return
lvalue_p_1 (TREE_OPERAND (ref, 0),

88

treat_class_rvalues_as_lvalues);

89

90

case
COMPONENT_REF:

91

op1_lvalue_kind = lvalue_p_1
(TREE_OPERAND (ref, 0),

92

treat_class_rvalues_as_lvalues);

93

/* In an
expression of the form "X.Y", the packed-ness of the

94

expression
does not depend on "X".
*/

95

op1_lvalue_kind &= ~clk_packed;

96

/* Look at the member designator.
*/

97

if (!op1_lvalue_kind

98

/* The
"field" can be a FUNCTION_DECL or an OVERLOAD in some

99

situations.
*/

100

|| TREE_CODE (TREE_OPERAND (ref, 1))
!= FIELD_DECL)

101

;

102

else if (DECL_C_BIT_FIELD (TREE_OPERAND
(ref, 1)))

103

{

104

/* Clear the
ordinary bit. If this object was a class

105

rvalue we
want to preserve that information.
*/

106

op1_lvalue_kind &= ~clk_ordinary;

107

/* The lvalue
is for a bitfield.
*/

108

op1_lvalue_kind |= clk_bitfield;

109

}

110

else if (DECL_PACKED (TREE_OPERAND (ref,
1)))

111

op1_lvalue_kind |= clk_packed;

112

113

return
op1_lvalue_kind;

114

115

case
STRING_CST:

116

return
clk_ordinary;

117

118

case
VAR_DECL:

119

if (TREE_READONLY (ref) && !
TREE_STATIC (ref)

120

&& DECL_LANG_SPECIFIC (ref)

121

&& DECL_IN_AGGR_P (ref))

122

return
clk_none;

123

case
INDIRECT_REF:

124

case
ARRAY_REF:

125

case
PARM_DECL:

126

case
RESULT_DECL:

127

if (TREE_CODE (TREE_TYPE (ref)) !=
METHOD_TYPE)

128

return
clk_ordinary;

129

break
;

130

131

/* A currently unresolved scope ref.
*/

132

case
SCOPE_REF:

133

abort ();

134

case
MAX_EXPR:

135

case
MIN_EXPR:

136

op1_lvalue_kind = lvalue_p_1
(TREE_OPERAND (ref, 0),

137

treat_class_rvalues_as_lvalues);

138

op2_lvalue_kind = lvalue_p_1
(TREE_OPERAND (ref, 1),

139

treat_class_rvalues_as_lvalues);

140

break
;

141

142

case
COND_EXPR:

143

op1_lvalue_kind = lvalue_p_1
(TREE_OPERAND (ref, 1),

144

treat_class_rvalues_as_lvalues);

145

op2_lvalue_kind = lvalue_p_1 (TREE_OPERAND
(ref, 2),

146

treat_class_rvalues_as_lvalues);

147

break
;

148

149

case
MODIFY_EXPR:

150

return
clk_ordinary;

151

152

case
COMPOUND_EXPR:

153

return
lvalue_p_1 (TREE_OPERAND (ref, 1),

154

treat_class_rvalues_as_lvalues);

155

156

case
TARGET_EXPR:

157

return
treat_class_rvalues_as_lvalues ? clk_class : clk_none;

158

159

case
CALL_EXPR:

160

case
VA_ARG_EXPR:

161

/* Any
class-valued call would be wrapped in a TARGET_EXPR.
*/

162

return
clk_none;

163

164

case
FUNCTION_DECL:

165

/* All
functions (except non-static-member functions) are

166

lvalues.
*/

167

return
(DECL_NONSTATIC_MEMBER_FUNCTION_P (ref)

168

? clk_none : clk_ordinary);

169

170

case
NON_DEPENDENT_EXPR:

171

/* We must
consider NON_DEPENDENT_EXPRs to be lvalues so that

172

things like
"&E" where "E" is an expression with a

173

non-dependent
type work. It is safe to be lenient because an

174

error will be
issued when the template is instantiated if "E"

175

is not an
lvalue.
*/

176

return
clk_ordinary;

177

178

default
:

179

break
;

180

}

181

182

/* If one operand is
not an lvalue at all, then this expression is

183

not an
lvalue.
*/

184

if (!op1_lvalue_kind || !op2_lvalue_kind)

185

return
clk_none;

186

187

/* Otherwise, it's
an lvalue, and it has all the odd properties

188

contributed by
either operand.
*/

189

op1_lvalue_kind = op1_lvalue_kind |
op2_lvalue_kind;

190

/* It's not an
ordinary lvalue if it involves either a bit-field or

191

a class
rvalue.
*/

192

if ((op1_lvalue_kind & ~clk_ordinary) !=
clk_none)

193

op1_lvalue_kind &= ~clk_ordinary;

194

return
op1_lvalue_kind;

195

}

上面的
NON_DEPENDENT_EXPR
是用于非类型依赖,但出现在模板中的表达式的占位符。看到

INDIRECT_REF
是左值(不过指针本身是右值,它是上面条款
3
所描述的情况);而普通的变量也是左值(
118
行);另外如果数组被引用,其引用是左值,否则该数组将被衰退为右值的指针类型。接着注意
149
行的
MODIFY_EXPR
,它毫无疑问是左值,而随后的
COMPOUND_EXPR
依赖于其第二个操作数。而对于可能包含左值操作数的表达式,
lvalue_p_1

递归入操作数。

值得注意的是,“
int& f();
”的调用表达式“
f();
”是一个左值,但是像“
f() = 5;
”这样的表达式是非法的;这个左值可以被直接使用的情况是把“
f()
”用作参数,
125
行的
PARM_DECL
覆盖了这个用法;因此不在
PARM_DECL
里的
CALL_EXPR
被视为右值。另外,
PARM_DECL

RESULT_DECL

ARRAY_REF

INDIRECT_REF
作为左值,其类型不能是类的非静态方法。

standard_conversion (continue)

524

if ((tcode == POINTER_TYPE ||
TYPE_PTR_TO_MEMBER_P (to))

525

&& expr && null_ptr_cst_p
(expr))

526

conv = build_conv
(STD_CONV, to, conv);

527

else if (tcode == POINTER_TYPE &&
fcode == POINTER_TYPE

528

&& TREE_CODE (TREE_TYPE (to))
== VECTOR_TYPE

529

&& TREE_CODE (TREE_TYPE (from))
== VECTOR_TYPE

530

&& ((*targetm

.vector_opaque_p) (TREE_TYPE
(to))

531

|| (*targetm

.vector_opaque_p)
(TREE_TYPE (from))))

532

conv = build_conv
(STD_CONV, to, conv);

533

else
if ((tcode == INTEGER_TYPE && fcode == POINTER_TYPE)

534

|| (tcode == POINTER_TYPE && fcode == INTEGER_TYPE))

535

{

536

/* For backwards
brain damage compatibility, allow interconversion of

537

pointers and
integers with a pedwarn.
*/

538

conv = build_conv
(STD_CONV, to, conv);

539

ICS_BAD_FLAG (conv) = 1;

540

}

541

else if (tcode == ENUMERAL_TYPE &&
fcode == INTEGER_TYPE)

542

{

543

/* For backwards
brain damage compatibility, allow interconversion of

544

enums and
integers with a pedwarn.
*/

545

conv = build_conv
(STD_CONV, to, conv);

546

ICS_BAD_FLAG (conv) = 1;

547

}

注意上面,指针与整数及整数与枚举值之间的转换,在
C++
的语言环境中,都是不良转换(不过在
C
下,仅会给出警告)。因此这些转换会被记录,并给予最低的等级。

standard_conversion (continue)

548

else if ((tcode == POINTER_TYPE &&
fcode == POINTER_TYPE)

549

|| (TYPE_PTRMEM_P (to) &&
TYPE_PTRMEM_P (from)))

550

{

551

tree to_pointee;

552

tree from_pointee;

553

554

if (tcode == POINTER_TYPE

555

&&
same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (from),

556

TREE_TYPE (to)))

557

;

558

else if (VOID_TYPE_P (TREE_TYPE (to))

559

&& !TYPE_PTRMEM_P (from)

560

&& TREE_CODE (TREE_TYPE
(from)) != FUNCTION_TYPE)

561

{

562

from = build_pointer_type

563

(cp_build_qualified_type
(void_type_node,

564

cp_type_quals (TREE_TYPE (from))));

565

conv = build_conv
(PTR_CONV, from, conv);

566

}

567

else if (TYPE_PTRMEM_P (from))

568

{

569

tree fbase = TYPE_PTRMEM_CLASS_TYPE
(from);

570

tree tbase = TYPE_PTRMEM_CLASS_TYPE (to);

571

572

if (DERIVED_FROM_P (fbase, tbase)

573

&&
(same_type_ignoring_top_level_qualifiers_p

574

(TYPE_PTRMEM_POINTED_TO_TYPE (from),

575

TYPE_PTRMEM_POINTED_TO_TYPE
(to))))

576

{

577

from = build_ptrmem_type (tbase,

578

TYPE_PTRMEM_POINTED_TO_TYPE (from));

579

conv = build_conv
(PMEM_CONV, from, conv);

580

}

581

else if (!same_type_p (fbase, tbase))

582

return
NULL;

583

}

584

else if (IS_AGGR_TYPE (TREE_TYPE (from))

585

&& IS_AGGR_TYPE (TREE_TYPE
(to))

586

/* [conv.ptr]

587

588

An rvalue of
type "pointer to cv D," where D is a

589

class type,
can be converted to an rvalue of type

590

"pointer
to cv B," where B is a base class (clause

591

_class.derived_) of D. If B is an inaccessible

592

(clause
_class.access_) or ambiguous

593

(_class.member.lookup_) base class of D, a program

594

that
necessitates this conversion is ill-formed.

*/

595

/* Therefore,
we use DERIVED_FROM_P, and not

596

ACESSIBLY_UNIQUELY_DERIVED_FROM_P,
in this test.
*/

597

&& DERIVED_FROM_P (TREE_TYPE
(to), TREE_TYPE (from)))

598

{

599

from =

600

cp_build_qualified_type (TREE_TYPE
(to),

601

cp_type_quals
(TREE_TYPE (from)));

602

from = build_pointer_type
(from);

603

conv = build_conv
(PTR_CONV, from, conv);

604

}

605

606

if (tcode == POINTER_TYPE)

607

{

608

to_pointee = TREE_TYPE (to);

609

from_pointee = TREE_TYPE (from);

610

}

611

else

612

{

613

to_pointee = TYPE_PTRMEM_POINTED_TO_TYPE
(to);

614

from_pointee =
TYPE_PTRMEM_POINTED_TO_TYPE (from);

615

}

616

617

if (same_type_p (from, to))

618

/* OK */
;

619

else if (comp_ptr_ttypes (to_pointee,
from_pointee))

620

conv = build_conv
(QUAL_CONV, to, conv);

621

else if (expr && string_conv_p (to,
expr, 0))

622

/* converting
from string constant to char *.
*/

623

conv = build_conv
(QUAL_CONV, to, conv);

624

else if (ptr_reasonably_similar
(to_pointee, from_pointee))

625

{

626

conv = build_conv
(PTR_CONV, to, conv);

627

ICS_BAD_FLAG (conv) = 1;

628

}

629

else

630

return
0;

631

632

from = to;

633

}

同样在【
3
】条文
4.10
“指针转换”中,有以下段落。并注意到在函数的
499
行把左值转换为右值,上面的
conv

保存了这个转换。

1.

一个空指针常量是一个整型常量表达式(
5.19
)的右值,它被评估为整形值
0
。一个空指针常量可以被转换到一个指针类型;其结果是这个类型的空指针值,并且可以与其它指向对象或指向函数的指针值区分开来。
2
个相同类型的空指针值应该是相等的。一个空指针常量到
cv-
限定类型指针的转换是单个转换,而不是一个包含指针转换再加上限定转换(
4.4
)的转换序列。

2.

类型“
pointer to cv T
”,其中
T
是一个对象类型,的一个右值,可以被转换到类型“
pointer to cv void
”的一个右值。其结果指向类型
T
的对象所在内存位置的开头,仿佛该对象是类型
T
作为最后派生类的对象(
most
derived object (1.8) of type T
)(即,不是作为基类子对象)。

3.

类型“
pointer to cv D
”,其中
D
是一个类类型,的一个右值,可以被转换到类型“
pointer to cv B
”,其中
B

D
的一个基类(条文
10
),的一个右值。如果
B

D
的一个不可访问(条文
11
)或有二义性(
10.2
)的基类,必须这个转换的程序是非法的。转换的结果是指向派生类对象的基类子对象的指针。空指针值被转换为目标类型的空指针值。

条款
1
由上面
526
行代码实现。它是单个
STD_CONV
。而条款
2
的条件由
558 ~ 560
行的代码来测试。
584 ~ 597
行的条件则实现条款
3


条文
4.11
“成员指针的转换”显示如下。

1.

一个空指针常量(
4.10
)可以被转换到成员指针;其结果是这个类型的空成员指针值,它与其它从非空指针常量构建的成员指针区分开来。两个相同类型的空成员指针应该是相等的。从一个空指针常量到具有
cv-
限定类型的成员指针的转换是单个转换,而不是包含成员指针转换,及限定转换(
4.4
)的转换序列。

2.

类型“
pointer to member of B of type cv T
”,其中
B
是一个类类型,的一个右值,可以被转换到类型“
pointer to member of D of type cv T
”,其中
D

B
的派生类(条文
10
),的一个右值。如果
B

D
的一个不可访问(条文
11
),或有二义性(
10.2
)或虚拟(
10.1
)的基类,必须这个转换的程序是非法的。转换的结果指向相同的成员,就像转换发生前的成员指针,不过它指向基类的成员,就好像它是派生类的一个成员。该结果指向
D
实例中
B
的成员。因为结果具有类型“
pointer to member of
D of type cv T
”,它可以通过一个
D
对象解引用(
dereference
)。其结果相当于
B
的成员指针被
D

B
的子对象解引用。空成员指针值被转换到目标类型的空成员指针值。
[
成员指针转换的规则(从基类成员指针到派生类成员指针)看起来与指向对象指针的规则相反(从派生类指针到基类指针)(
4.10
,条文
10
)。为了确保类型安全,这个转换是必要的。注意到一个成员指针不是一个对象指针或函数指针,并且这些指针的转换规则不适用于成员指针。特别的,一个成员指针不能被转换到一个
void*

]

同样条款
1
被上面
526
行的代码所覆盖。而条款
2

567
行的代码块所处理。来到
617
行,经过上面的转换序列,现在
from


to

应该或者是相同的类型,或者仅是
cv-
限定符的不同;否则,意味着没有可用的转换。

下面方法指针之间的转换类似于成员指针之间的转换。注意到
fromfn


tofn

分别是所指向的函数;
fbase


tbase

是各自的隐含的
this

指针。因此能执行的转换是:类型“
pointer to method of
B of type cv T
”,其中
B
是一个类类型,的一个右值,可用被转换到类型“
pointer to the
same
method of D of type cv T
”,其中
D

B
的一个派生类,的一个右值。并且看到在这里“相同”的含义要比重载中更严格些。

standard_conversion (continue)

634

else if (TYPE_PTRMEMFUNC_P (to) &&
TYPE_PTRMEMFUNC_P (from))

635

{

636

tree fromfn = TREE_TYPE
(TYPE_PTRMEMFUNC_FN_TYPE (from));

637

tree tofn = TREE_TYPE
(TYPE_PTRMEMFUNC_FN_TYPE (to));

638

tree fbase = TREE_TYPE (TREE_VALUE
(TYPE_ARG_TYPES (fromfn)));

639

tree tbase = TREE_TYPE (TREE_VALUE
(TYPE_ARG_TYPES (tofn)));

640

641

if (!DERIVED_FROM_P (fbase, tbase)

642

|| !same_type_p (TREE_TYPE (fromfn),
TREE_TYPE (tofn))

643

|| !compparms (TREE_CHAIN
(TYPE_ARG_TYPES (fromfn)),

644

TREE_CHAIN (TYPE_ARG_TYPES
(tofn)))

645

|| cp_type_quals (fbase) !=
cp_type_quals (tbase))

646

return
0;

647

648

from = cp_build_qualified_type (tbase,
cp_type_quals (fbase));

649

from = build_method_type_directly

(from,

650

TREE_TYPE
(fromfn),

651

TREE_CHAIN
(TYPE_ARG_TYPES (fromfn)));

652

from = build_ptrmemfunc_type (build_pointer_type
(from));

653

conv = build_conv
(PMEM_CONV, from, conv);

654

}


3
】条文
4.12
“布尔型转换”对以下的转换给出了指引。

1.

算术类型,枚举类型,指针类型,或成员指针类型的一个右值可以被转换为一个布尔类型的右值。一个
0
值,空指针值,或空成员指针值被转换为
false
;其他值被转换为
true


不过,指针到布尔值的转换等级不如整数到布尔值的转换。

standard_conversion (continue)

655

else if (tcode == BOOLEAN_TYPE)

656

{

657

/* [conv.bool]

658

659

An rvalue of
arithmetic, enumeration, pointer, or pointer to

660

member type can
be converted to an rvalue of type bool.

*/

661

if (ARITHMETIC_TYPE_P (from)

662

||
fcode == ENUMERAL_TYPE

663

|| fcode == POINTER_TYPE

664

|| TYPE_PTR_TO_MEMBER_P (from))

665

{

666

conv = build_conv
(STD_CONV, to, conv);

667

if (fcode == POINTER_TYPE

668

|| TYPE_PTRMEM_P (from)

669

|| (TYPE_PTRMEMFUNC_P (from)

670

&& ICS_STD_RANK (conv) <
PBOOL_RANK))

671

ICS_STD_RANK (conv) = PBOOL_RANK;

672

return
conv;

673

}

674

675

return
NULL_TREE;

676

}

那么下面的代码处理由【
3
】,条文
4.5
“整型提升”,
4.6
“浮点型提升”,
4.7
“整型转换”,
4.8
“浮点型转换”,及
4.9
“浮点整型转换”所描述的转换。下面的段落是其细节。

1.

类型
char


signed char


unsigned char


short int

,或
unsigned short int

的一个右值可以被转换为类型
int

的一个右值,如果
int

可以表示源类型的所有值;否则,源右值可以被转换为类型
unsigned int

的一个右值。

2.

类型
wchar_t


3.9.1
)或枚举类型(
7.2
)的一个右值可以被转换为以下类型中第一个可以表示源类型所有值的类型的一个右值:
int


unsigned int


long

,或
unsigned long



3.

一个整型位域(
9.6
)的右值可以被转换为类型
int

的一个右值,如果
int

可以表示该位域的所有值;否则,它可以被转换为类型
unsigned int

,如果
unsigned int

可以表示其所有值。如果位域还要大,不会对其应用整型提升。如果该位域具有枚举的类型,出于提升的目的,它亦被视为该类型的其他值。

4.

类型
bool

的一个右值可以被转换为类型
int

的一个右值,
false
成为
0

true
成为
1


5.

这些转换被称为整型提升(
integral
promotion
)。

1.

类型
float

的一个右值可以被转换为类型
double

的一个右值。其值不变。

2.

这个转换被称为浮点提升(
floating
point promotion
)。

1.

一个整型的右值可以被转换为另一个整型的右值。一个枚举类型的右值可以被转换为一个整型的右值。

2.

如果目标类型是无符号的,其结果值是与源整数同余的(
congruent
)最小无符号整数。(模
2n

,其中
n
是用于表示该无符号类型的比特数)。
[
注意:在一个
2
进制补码表示中,这个转换只是概念上的,位模式(
bit pattern
)没有变化(如果没有发生截断)。
]

3.

如果目标类型是有符号的,值不会改变,如果目标类型(及位域宽度)能表示它;否则,其值取决于编译器实现。

4.

如果目标类型是
bool

,参见
4.12
。如果源类型是
bool

,值
false
被转换为
0
,值
true
被转换为
1


5.

整形提升不包括在整形转换中。

1.

一个浮点类型的右值可以被转换到另一个浮点类型的右值。如果源值可以被目标类型精确表示,转换的结果就是这个精确表示。如果源值位于目标类型所能表示的
2
个相邻值之间,转换的结果依赖于编译器实现对两者的选择。否则,其行为是未定义的。

2.

浮点提升不包括在浮点转换中。

1.

一个浮点类型的右值可以被转换为一个整形的右值。该转换就是截断;即,小数部分被舍弃。如果截断后的值不能被目标类型表示,其行为是未定义的。
[
注意:如果目标类型是
bool
,参见
4.12

]

2.

一个整形或枚举类型的右值可以被转换为一个浮点类型的右值。如果可能,其结果是精确的。否则,依赖于编译器的实现,选择次高或次低的表示值。
[
注意:如果该整数不能被该浮点类型的一个值精确表示,会发生精度丢失
]
。如果源类型是
bool
,值
false
被转换为
0
而值
true
转换为
1


679

690
行的代码覆盖了上面所提及的所有的情形。因为
PROMO_RANK
要优于
STD_RANK
,编译器总是尽可能使用
PROMO_RANK


standard_conversion (continue)

677

/* We don't check
for ENUMERAL_TYPE here because there are no standard

678

conversions to
enum type.
*/

679

else if (tcode == INTEGER_TYPE || tcode ==
BOOLEAN_TYPE

680

|| tcode == REAL_TYPE)

681

{

682

if (! (INTEGRAL_CODE_P (fcode) || fcode ==
REAL_TYPE))

683

return
0;

684

conv = build_conv
(STD_CONV, to, conv);

685

686

/* Give this a
better rank if it's a promotion.
*/

687

if (same_type_p (to, type_promotes_to
(from))

688

&& ICS_STD_RANK (TREE_OPERAND
(conv, 0)) <= PROMO_RANK)

689

ICS_STD_RANK (conv) = PROMO_RANK;

690

}

691

else if (fcode == VECTOR_TYPE &&
tcode == VECTOR_TYPE

692

&& ((*targetm

.vector_opaque_p) (from)

693

|| (*targetm

.vector_opaque_p) (to)))

694

return
build_conv
(STD_CONV, to, conv);

695

else if (!(flags &
LOOKUP_CONSTRUCTOR_CALLABLE)

696

&& IS_AGGR_TYPE (to)
&& IS_AGGR_TYPE (from)

697

&& is_properly_derived_from
(from, to))

698

{

699

if (TREE_CODE (conv) == RVALUE_CONV)

700

conv = TREE_OPERAND (conv, 0);

701

conv = build_conv
(BASE_CONV, to, conv);

702

/* The
derived-to-base conversion indicates the initialization

703

of a parameter
with base type from an object of a derived

704

type. A
temporary object is created to hold the result of

705

the
conversion.
*/

706

NEED_TEMPORARY_P (conv) = 1;

707

}

708

else

709

return
0;

710

711

return
conv;

712

}

695

707
行的代码处理派生类到基类的转换,它由条文“隐式转换序列”的条款
6
所定义。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: