现在的位置: 首页 > 综合 > 正文

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

2013年02月04日 ⁄ 综合 ⁄ 共 7769字 ⁄ 字号 评论关闭

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);

抱歉!评论已关闭.