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

5.13.1.2.3.1.  案例学习:到指针的转换

5.13.1.2.3.1.1.          从类到指针

到指针的转换是很有趣的,值得一看。处理这个转换的函数是下面的 cp_convert_to_pointer 。类可以被转换到指针类型,仅当它定义了一个这样做的用户定义转换。

 

76      static tree

77      cp_convert_to_pointer (tree type, tree expr, bool force)                                     in cvt.c

78      {

79        tree intype = TREE_TYPE (expr);

80        enum tree_code form;

81        tree rval;

82        if (intype == error_mark_node)

83          return error_mark_node;

84     

85        if (IS_AGGR_TYPE (intype))

86        {

87          intype = complete_type (intype);

88          if (!COMPLETE_TYPE_P (intype))

89          {

90             error ("can't convert from incomplete type `%T' to `%T'",

91                   intype, type);

92             return error_mark_node;

93          }

94     

95          rval = build_type_conversion (type, expr);

96          if (rval)

97          {

98             if (rval == error_mark_node)

99               error ("conversion of `%E' from `%T' to `%T' is ambiguous",

100                   expr, intype, type);

101           return rval;

102        }

103      }

 

函数 build_type_conversion build_user_type_conversion 的定义如下。显然它查找这个用户定义的转换操作符,并产生执行转换的代码。

 

995    tree

996    build_type_conversion (tree xtype, tree expr)                                                    in cvt.c

997    {

998      /* C++: check to see if we can convert this aggregate type

999        into the required type.  */

1000     return build_user_type_conversion (xtype, expr, LOOKUP_NORMAL);

1001   }

 

2522   tree

2523   build_user_type_conversion (tree totype, tree expr, int flags)                              in call.c

2524   {

2525     struct z_candidate *cand

2526       = build_user_type_conversion_1 (totype, expr, flags);

2527  

2528     if (cand)

2529     {

2530       if (TREE_CODE (cand->second_conv) == AMBIG_CONV)

2531         return error_mark_node;

2532       return convert_from_reference (convert_like (cand->second_conv, expr));

2533     }

2534     return NULL_TREE;

2535   }

5.13.1.2.3.1.2.          void* 或函数指针

那么在下面的 type 是转换的目标类型, 106 行的条件为“ void* ”或函数指针的 type 所满足。而 intype 是表达式 expr 的类型。

 

cp_convert_to_pointer (continue)

 

105      /* Handle anachronistic conversions from (::*)() to cv void* or (*)().  */

106      if (TREE_CODE (type) == POINTER_TYPE

107         && (TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE

108               || VOID_TYPE_P (TREE_TYPE (type))))

109      {

110         /* Allow an implicit this pointer for pointer to member

111            functions.  */

112         if (TYPE_PTRMEMFUNC_P (intype))

113          {

114            if (pedantic || warn_pmf2ptr )

115              pedwarn ("converting from `%T' to `%T'", intype, type);

116            if (TREE_CODE (expr) == PTRMEM_CST)

117              expr = build_address (PTRMEM_CST_MEMBER (expr));

118            else

119            {

120             tree decl = maybe_dummy_object (TYPE_PTRMEM_CLASS_TYPE (intype),

121                                          0);

122             decl = build_address (decl);

123             expr = get_member_function_from_ptrfunc (&decl, expr);

124           }

125        }

126        else if (TREE_CODE (TREE_TYPE (expr)) == METHOD_TYPE)

127        {

128           if (pedantic || warn_pmf2ptr )

129             pedwarn ("converting from `%T' to `%T'", intype, type);

130           expr = build_addr_func (expr);

131        }

132        if (TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE)

133          return build_nop (type, expr);

134        intype = TREE_TYPE (expr);

135      }

 

如果 intype 是方法指针的类型,这个转换可能是危险的代码;因此给出警告。而如果 expr 是一个 PTRMEM_CST ——一个成员指针常量,只需要获取这个成员的地址。不过,对于 expr 是指向方法的指针的情况,要求在调用 get_member_function_from_ptrfunc 中有隐含的 this 指针实参来解析这个指针。因而需要调用 maybe_dummy_object 来构建该类型用于解析的假的实例。

 

1717 tree

1718 maybe_dummy_object (tree type, tree* binfop)                                            in cp/tree.c

1719 {

1720    tree decl, context;

1721    tree binfo;

1722   

1723    if (current_class_type

1724       && (binfo = lookup_base (current_class_type , type,

1725                              ba_ignore | ba_quiet, NULL)))

1726      context = current_class_type ;

1727    else

1728    {

1729      /* Reference from a nested class member function.  */

1730      context = type;

1731      binfo = TYPE_BINFO (type);

1732    }

1733

1734    if (binfop)

1735       *binfop = binfo;

1736   

1737    if (current_class_ref && context == current_class_type

1738        /* Kludge: Make sure that current_class_type is actually

1739          correct. It might not be if we're in the middle of

1740          tsubst_default_argument.  */

1741        && same_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (current_class_ref )),

1742                        current_class_type ))

1743      decl = current_class_ref ;

1744    else

1745      decl = build_dummy_object (context);

1746

1747    return decl;

1748 }

 

首先,为这个假的对象设立相应的上下文;如果“ this ”指针是可用的,就把它用作这个假对象;否则,通过下面的函数构建该对象的一个 NULL 指针。

 

1706 tree

1707 build_dummy_object (tree type)                                                                  in cp/tree.c

1708 {

1709    tree decl = build1 (NOP_EXPR, build_pointer_type (type), void_zero_node);

1710    return build_indirect_ref (decl, NULL);

1711 }

5.13.1.2.3.1.3.          在类指针之间

如果源类型及目标类型都是指向类的指针,它们必须有继承关系;否则就是一个错误。而如果目标类型是指向方法的指针,把其他类型的指针转换到它是不允许的。那么对于标量指针之间,及标量指针与类指针之间的转换,只需为该转换构建 NOP_EXPR

这包括了【 3 】条文 5.2.10 Reinterpret_cast ”,条款 7 所定义的情况:

7. 指向一个对象的指针可用被显式地转换到一个不同类型的对象。除了转换类型为“ T1 指针类型”的一个右值到类型“ T2 指针类型”(其中 T1 T2 是对象类型,并且 T2 所要求的对齐不小于 T1 ),然后回到其初始类型,产生初始的指针值之外;这样的一个指针转换的结果是未定义的。

 

cp_convert_to_pointer (continue)

 

137      if (expr == error_mark_node)

138        return error_mark_node;

139   

140      form = TREE_CODE (intype);

141   

142      if (POINTER_TYPE_P (intype))

143      {

144        intype = TYPE_MAIN_VARIANT (intype);

145   

146        if (TYPE_MAIN_VARIANT (type) != intype

147            && TREE_CODE (type) == POINTER_TYPE

148            && TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE

149            && IS_AGGR_TYPE (TREE_TYPE (type))

150            && IS_AGGR_TYPE (TREE_TYPE (intype))

151            && TREE_CODE (TREE_TYPE (intype)) == RECORD_TYPE)

152        {

153           enum tree_code code = PLUS_EXPR;

154           tree binfo;

155           tree intype_class;

156           tree type_class;

157           bool same_p;

158   

159           intype_class = TREE_TYPE (intype);

160           type_class = TREE_TYPE (type);

161   

162            same_p = same_type_p (TYPE_MAIN_VARIANT (intype_class),

163                              TYPE_MAIN_VARIANT (type_class));

164          binfo = NULL_TREE;

165           /* Try derived to base conversion.  */

166           if (!same_p)

167             binfo = lookup_base (intype_class, type_class, ba_check, NULL);

168           if (!same_p && !binfo)

169           {

170             /* Try base to derived conversion.  */

171             binfo = lookup_base (type_class, intype_class, ba_check, NULL);

172             code = MINUS_EXPR;

173           }

174           if (binfo == error_mark_node)

175             return error_mark_node;

176           if (binfo || same_p)

177           {

178             if (binfo)

179              expr = build_base_path (code, expr, binfo, 0);

180             /* Add any qualifier conversions.  */

181             return build_nop (type, expr);

182           }

183        }

184   

185        if (TYPE_PTRMEMFUNC_P (type))

186        {

187           error ("cannot convert `%E' from type `%T' to type `%T'",

188                 expr, intype, type);

189           return error_mark_node;

190        }

191   

192        return build_nop (type, expr);

193      }

5.13.1.2.3.1.4.          指向成员的指针之间

下面, TYPE_PTRMEM_P 成立,如果这是一个指向数据成员的指针。

注意到当到达这里,该语句一定通过了解析过程中的语法检查;因此 TREE_TYPE (type) 必定匹配下面的 TREE_TYPE (intype) 。在上面的例子中,该转换可以被隐式地执行;除非该转换是自虚拟基类——这只能通过 reinterpret_cast 来完成,正如【 3 】条文 5.2.10 Reinterpret_cast ”,条款 3 定义的:“ reinterpret_cast 执行的映射是由实现定义的。 [ 注意:它可能,或可能不, 产生一个不同于原始值的表达 ] ”。 GCC 选择对于这种 自虚拟基类的 转换不做任何事。考虑下面的例子:

class C {};

class D {

public :

   C dc;

};

class B: public virtual D {

public :

   C bc;

};

C* func (B* b) { return &b->bc; }

int main () {

    D d;

    func (&d);

    return 0;

}

编译器给出如下的消息(它违反了【 3 】条文 4.11 “成员指针的转换”,条款 2 ):

test2.cpp: In function ‘int main()’:

test2.cpp:17: error: invalid conversion from ‘D*’ to ‘B*’

test2.cpp:17: error:   initializing argument 1 of ‘C* func(B*)’

test2.cpp:17: error: cannot convert from base ‘D’ to derived type ‘B’ via virtual base ‘D’

前两个 error 是在 convert_like_real 3944 3946 行给出的;最后一个则是在这里。

如果 type intype 没有继承关系,注意到下面在 204 207 行的 lookup_base 将返回 NULL (如果基类是不可访问或具二义性,将返回 error_mark_node ),这也是仅为 reinterpret_cast 所允许的情形,如下面【 3 】的条文 5.2.10 Reinterpret_cast ”:

9. 一个“指向具有类型 T1 X 成员的指针”的右值,可以被显式地转换到一个“指向具有类型 T2 Y 成员的指针”的右值,如果 T1 T2 都是函数类型或两者都是对象类型。空成员指针值( 4.11 )可以被转换到目的类型的空成员指针值。这个转换的结果是不确定的,除了以下情形以外:

转换“成员函数指针”类型的一个右值到另外的成员函数指针类型,再转换回原始类型,产生原始的成员指针值。

转换 “指向具有类型 T1 X 成员的指针”类型的一个右值到“指向具有类型 T2 Y 成员的指针”的类型(其中 T2 的对齐要求不强于 T1 no stricter than )),再转换回原始类型,产生原始的成员指针值。

看到其结果是不确定的,这就是为什么 reinterpret_cast 是一个危险的操作符,它应该被小心使用。对于 type intype 不具有继承关系的情形, GCC 236 行为之构建了 NOP_EXPR 。并且注意到如果我们能来到这里,相应的转换表达式必定通过了语法检查。

 

cp_convert_to_pointer (continue)

 

194      else if (TYPE_PTRMEM_P (type) && TYPE_PTRMEM_P (intype))

195      {

196        tree b1;

197        tree b2;

198        tree binfo;

199        enum tree_code code = PLUS_EXPR;

200        base_kind bk;

201   

202        b1 = TYPE_PTRMEM_CLASS_TYPE (type);

203        b2 = TYPE_PTRMEM_CLASS_TYPE (intype);

204        binfo = lookup_base (b1, b2, ba_check, &bk);

205        if (!binfo)

206        {

207           binfo = lookup_base (b2, b1, ba_check, &bk);

208           code = MINUS_EXPR;

209        }

210        if (binfo == error_mark_node)

211           return error_mark_node;

212   

213        if (bk == bk_via_virtual)

214        {

215           if (force)

216             warning ("pointer to member cast from `%T' to `%T' is via virtual base",

217                      intype, type);

218           else

219           {

220             error ("pointer to member cast from `%T' to `%T' is via virtual base",

221                   intype, type);

222             return error_mark_node;

223           }

224           /* This is a reinterpret cast, whose result is unspecified.

225             We choose to do nothing.  */

226           return build1 (NOP_EXPR, type, expr);

227        }

228   

229        if (TREE_CODE (expr) == PTRMEM_CST)

230          expr = cplus_expand_constant (expr);

231   

232        if (binfo && !integer_zerop (BINFO_OFFSET (binfo)))

233          expr = size_binop (code,

234                           build_nop (sizetype, expr),

235                           BINFO_OFFSET (binfo));

236        return build_nop (type, expr);

237      }

238      else if (TYPE_PTRMEMFUNC_P (type) && TYPE_PTRMEMFUNC_P (intype))

239        return build_ptrmemfunc (TYPE_PTRMEMFUNC_FN_TYPE (type), expr, 0);

 

而如果这些类型具有继承关系,前端会相应地调整类实例。

5.13.1.2.3.1.5.          从成员函数指针到其它指针类型

那么下面的代码处理以下与 reinterpret_cast 相关的情形:

4. 一个指针可用被显式地转换到任意足够大保存它的整数类型。其映射函数是实现定义的 [ 注意:对于那些知道底层机器寻址结构的人来说,这不足为奇 ]

5. 一个整数类型或枚举类型的值可以被显式地转换到一个指针。一个指针转换到足够大小的一个整数(如果在实现中有这样对象的存在),然后转换回同样的指针类型,将得到其原始值;指针与整数映射的其他方面则是由实现确定的。

6. 指向一个函数的指针可用被显式地转换到指向另一个函数类型的指针。通过一个指向别的函数类型的指针( 8.3.5 )来调用这个函数,其行为是未定义的。除了把类型“ T1 指针”的一个右值转换到“ T2 指针”类型(其中 T1 T2 都是函数类型),然后转换回其原始类型,产生原始的指针值之外,这样的一个指针转换的结果是不确定的。 [ 注意:参考 4.10 ,关于更多指针转换的细节 ]

8. 空指针值( 4.10 )被转换到目的类型的空指针值。

下面的 type 是目的类型,而 form 应该是“ from ”的笔误。注意到成员指针不能保存在任一整数类型中,因为事实上,它包含了两个部分——对象及成员。

 

cp_convert_to_pointer (continue)

 

240      else if (TYPE_PTRMEMFUNC_P (intype))

241      {

242        if (!warn_pmf2ptr )

243        {

244           if (TREE_CODE (expr) == PTRMEM_CST)

245             return cp_convert_to_pointer (type,

246                                      PTRMEM_CST_MEMBER (expr),

247                                      force);

248           else if (TREE_CODE (expr) == OFFSET_REF)

249           {

250             tree object = TREE_OPERAND (expr, 0);

251             return get_member_function_from_ptrfunc (&object,

252                                                 TREE_OPERAND (expr, 1));

253           }

254        }

255        error ("cannot convert `%E' from type `%T' to type `%T'",

256              expr, intype, type);

257        return error_mark_node;

258      }

259   

260      if (integer_zerop (expr))

261      {

262        if (TYPE_PTRMEMFUNC_P (type))

263          return build_ptrmemfunc (TYPE_PTRMEMFUNC_FN_TYPE (type), expr, 0);

264   

265        if (TYPE_PTRMEM_P (type))

266          /* A NULL pointer-to-member is represented by -1, not by

267             zero.  */

268          expr = build_int_2 (-1, -1);

269        else

270          expr = build_int_2 (0, 0);

271        TREE_TYPE (expr) = type;

272        /* Fix up the representation of -1 if appropriate.  */

273        force_fit_type (expr, 0);

274        return expr;

275      }

276      else if (TYPE_PTR_TO_MEMBER_P (type) && INTEGRAL_CODE_P (form))

277      {

278        error ("invalid conversion from '%T' to '%T'", intype, type);

279        return error_mark_node;

280      }

281   

282      if (INTEGRAL_CODE_P (form))

283      {

284        if (TYPE_PRECISION (intype) == POINTER_SIZE)

285          return build1 (CONVERT_EXPR, type, expr);

286        expr = cp_convert (c_common_type_for_size (POINTER_SIZE, 0), expr);

287        /* Modes may be different but sizes should be the same.  */

288        if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (expr)))

289             != GET_MODE_SIZE (TYPE_MODE (type)))

290          /* There is supposed to be some integral type

291             that is the same width as a pointer.  */

292          abort ();

293        return convert_to_pointer (type, expr);

294      }

295   

296      if (type_unknown_p (expr))

297        return instantiate_type (type, expr, tf_error | tf_warning);

298   

299      error ("cannot convert `%E' from type `%T' to type `%T'",

300             expr, intype, type);

301      return error_mark_node;

302    }

 

现在回到 ocp_convert 中的过程。注意下面的 e 来自 expr ,因此 720 行的 dtype 是表达式的类型;而 code 是目的类型的节点编码。那么如果目的类型是类类型,如 729 行所示,如果源类型与目的类型具有继承关系,或者源类型定义了到目的类型的转换操作符,这个转换是可能的。

 

ocp_convert (continue)

 

705    (code == VECTOR_TYPE)

706        return fold (convert_to_vector (type, e));

707      if (code == REAL_TYPE || code == COMPLEX_TYPE)

708      {

709        if (IS_AGGR_TYPE (TREE_TYPE (e)))

710        {

711           tree rval;

712          rval = build_type_conversion (type, e);

713          if (rval)

714            return rval;

715          else

716            if (flags & LOOKUP_COMPLAIN)

717              error ("`%#T' used where a floating point value was expected",

718                   TREE_TYPE (e));

719        }

720        if (code == REAL_TYPE)

710          return fold (convert_to_real (type, e));

711         else if (code == COMPLEX_TYPE)

712          return fold (convert_to_complex (type, e));

713      }

714   

715      /* New C++ semantics: since assignment is now based on

716        memberwise copying, if the rhs type is derived from the

717        lhs type, then we may still do a conversion.  */

718      if (IS_AGGR_TYPE_CODE (code))

719      {

720        tree dtype = TREE_TYPE (e);

721        tree ctor = NULL_TREE;

722   

723        dtype = TYPE_MAIN_VARIANT (dtype);

724   

725        /* Conversion between aggregate types. New C++ semantics allow

726          objects of derived type to be cast to objects of base type.

727           Old semantics only allowed this between pointers.

728   

729           There may be some ambiguity between using a constructor

730           vs. using a type conversion operator when both apply.  */

731   

732        ctor = e;

733   

734        if (abstract_virtuals_error (NULL_TREE, type))

735          return error_mark_node;

736   

737        if ((flags & LOOKUP_ONLYCONVERTING)

738           && ! (IS_AGGR_TYPE (dtype) && DERIVED_FROM_P (type, dtype)))

739          /* For copy-initialization, first we create a temp of the proper type

740             with a user-defined conversion sequence, then we direct-initialize

741              the target with the temp (see [dcl.init]).  */

742          ctor = build_user_type_conversion (type, ctor, flags);

743        else

744          ctor = build_special_member_call (NULL_TREE,

745                                       complete_ctor_identifier ,

746                                      build_tree_list (NULL_TREE, ctor),

747                                      TYPE_BINFO (type), flags);

748        if (ctor)

749          return build_cplus_new (type, ctor);

750      }

751   

752      if (flags & LOOKUP_COMPLAIN)

753        error ("conversion from `%T' to non-scalar type `%T' requested",

754             TREE_TYPE (expr), type);

755      if (flags & LOOKUP_SPECULATIVELY)

756        return NULL_TREE;

757      return error_mark_node;

758    }

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值