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

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 所定义。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值