概述
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 }
最后
以上就是开放蜡烛为你收集整理的GCC-3.4.6源代码学习笔记(154)的全部内容,希望文章能够帮你解决GCC-3.4.6源代码学习笔记(154)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复