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

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

2011-01-29 15:06 405 查看
5.12.5.2.2.2.2.

缺省实参

如果耗尽了
arg

,但
parm

还没有,
parm

必然包含了缺省实参,该形参列表应该以特殊节点
void_list_node

来结尾。那么在对应的节点中,
TREE_VALUE
保存了该类型,而
TREE_PURPOSE
是缺省实参的表达式。

4258

tree

4259

convert_default_arg

(tree type, tree arg, tree fn, int parmnum)
in call.c

4260

{

4261

/*
If the ARG is an unparsed default argument expression, the

4262

conversion cannot be performed.
*/

4263

if (TREE_CODE (arg) ==
DEFAULT_ARG)

4264

{

4265

error ("the default
argument for parameter %d of `%D' has "

4266

"not yet been
parsed",

4267

parmnum, fn);

4268

return
error_mark_node;

4269

}

4270

4271

if (fn &&
DECL_TEMPLATE_INFO (fn))

4272

arg =
tsubst_default_argument (fn, type, arg);

4273

4274

arg = break_out_target_exprs
(arg);

4275

4276

if (TREE_CODE (arg) ==
CONSTRUCTOR)

4277

{

4278

arg = digest_init
(type, arg, 0);

4279

arg =
convert_for_initialization (0, type, arg, LOOKUP_NORMAL,

4280

"default
argument", fn, parmnum);

4281

}

4282

else

4283

{

4284

/*
This could get clobbered by the following call.

*/

4285

if (TREE_HAS_CONSTRUCTOR
(arg))

4286

arg = copy_node (arg);

4287

4288

arg =
convert_for_initialization (0, type, arg, LOOKUP_NORMAL,

4289

"default
argument", fn, parmnum);

4290

arg = convert_for_arg_passing
(type, arg);

4291

}

4292

4293

return
arg;

4294

}

节点
DEFAULT_ARG
是为未解析的缺省实参所构建的。记得在解析类定义的过程中,缺省实参被
DEFAULT_ARG
所缓存,它将在该解析结束后才解析。因此在这里不应该出现
DEFAULT_ARG
(如果是这样,这可能是在类定义中缺少了“
};
”),而且在这一点上,前端不知道如何处理这种节点。

那么在
4274
传递给下面函数的
arg

,是由该函数所有调用所共享的缺省实参。不过,正如【
3
】所定义的,“每次调用该函数都要评估缺省实参
”,因此在真正评估这个缺省实参之前,需要
准备合适的
arg

的拷贝(我们不能直接改变
arg

),并如下所示地更新这个局部临时对象。

1259

tree

1260

break_out_target_exprs

(tree t)
in
cp/tree.c

1261

{

1262

static
int target_remap_count;

1263

static
splay_tree target_remap;

1264

1265

if (!target_remap_count++)

1266

target_remap =
splay_tree_new (splay_tree_compare_pointers,

1267

/*splay_tree_delete_key_fn=*/
NULL,

1268

/*splay_tree_delete_value_fn=*/
NULL);

1269

walk_tree
(&t, bot_manip
, target_remap, NULL);

1270

walk_tree
(&t, bot_replace
, target_remap, NULL);

1271

1272

if (!--target_remap_count)

1273

{

1274

splay_tree_delete
(target_remap);

1275

target_remap = NULL;

1276

}

1277

1278

return
t;

1279

}

上面,
walk_tree

遍历以
t

为根的子树,并在以
t

的编码所选出的节点上执行给定的函数。在第一次遍历中,使用下面的函数。注意该函数总是返回
NULL
(下面的
copy_tree_r

返回
NULL
)来强制
walk_tree

执行深度优先的完整遍历(即,
tp

可能是一个
tree_list
,其中的节点可以包含操作数,它们依次亦可能是
tree_list
,等等,访问将从底部开始向上);不过是否进入子树(即操作数)由局部变量
walk_subtrees

来控制(它在下面的函数中作为实参
walk_subtrees

传入)。在使用指定的函数处理树节点前,
walk_subtrees

被设置为
1
;是指定的函数来决定该节点是否是感兴趣的,并且需要进入。

1182

static
tree

1183

bot_manip

(tree* tp, int* walk_subtrees, void* data)
in cp/tree.c

1184

{

1185

splay_tree target_remap =
((splay_tree) data);

1186

tree t = *tp;

1187

1188

if (TREE_CONSTANT (t))

1189

{

1190

/*
There can't be any TARGET_EXPRs or their slot variables below

1191

this
point. We used to check !TREE_SIDE_EFFECTS, but then we

1192

failed to copy an ADDR_EXPR of the slot
VAR_DECL.
*/

1193

*walk_subtrees = 0;

1194

return
NULL_TREE;

1195

}

1196

if (TREE_CODE (t) ==
TARGET_EXPR)

1197

{

1198

tree u;

1199

1200

if (TREE_CODE
(TREE_OPERAND (t, 1)) == AGGR_INIT_EXPR)

1201

{

1202

mark_used
(TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 1), 0), 0));

1203

u = build_cplus_new

1204

(TREE_TYPE (t),
break_out_target_exprs (TREE_OPERAND (t, 1)));

1205

}

1206

else

1207

{

1208

u = build_target_expr_with_type

1209

(break_out_target_exprs
(TREE_OPERAND (t,
1)), TREE_TYPE (t));

1210

}

1211

1212

/* Map the old variable to the new one.
*/

1213

splay_tree_insert
(target_remap,

1214

(splay_tree_key)
TREE_OPERAND (t, 0),

1215

(splay_tree_value)
TREE_OPERAND (u, 0));

1216

1217

/*
Replace the old expression with the new version.
*/

1218

*tp = u;

1219

/*
We don't have to go below this point; the recursive call to

1220

break_out_target_exprs will have handled
anything below this

1221

point.

*/

1222

*walk_subtrees = 0;

1223

return
NULL_TREE;

1224

}

1225

else if (TREE_CODE (t) ==
CALL_EXPR)

1226

mark_used
(TREE_OPERAND (TREE_OPERAND (t, 0), 0));

1227

1228

/*
Make a copy of this node.
*/

1229

return
copy_tree_r
(tp, walk_subtrees, NULL);

1230

}

对于常量或
TRAGET_EXPR
以外的节点,
copy_tree_r

拷贝这个节点,如果它是
*_CST
(看到它其实被上面的
TREE_CONSTANT
滤掉了),或表达式,或
TREE_LIST
,或
TREE_VEC
,或
OVERLOAD
(由下面的
C++
的钩子
tree_chain_matters_p

来辨别,并注意到在这里
walk_subtrees

没有改变,
walk_tree

将进入该节点的子节点,并继续拷贝其结构)。

1966

tree

1967

copy_tree_r

(tree *tp, int
*walk_subtrees, void *data ATTRIBUTE_UNUSED)

in tree-inline.c

1968

{

1969

enum
tree_code code = TREE_CODE (*tp);

1970

1971

/* We make copies of most nodes.
*/

1972

if (IS_EXPR_CODE_CLASS
(TREE_CODE_CLASS (code))

1973

|| TREE_CODE_CLASS
(code) == 'c'

1974

|| code == TREE_LIST

1975

|| code == TREE_VEC

1976

|| (*lang_hooks

.tree_inlining.tree_chain_matters_p)
(*tp))

1977

{

1978

/* Because the chain gets clobbered when we
make a copy, we save it

1979

here.
*/

1980

tree chain = TREE_CHAIN
(*tp);

1981

1982

/*
Copy the node.
*/

1983

*tp = copy_node (*tp);

1984

1985

/*
Now, restore the chain, if appropriate. That will cause

1986

walk_tree to walk into the chain as
well.
*/

1987

if (code == PARM_DECL ||
code == TREE_LIST

1988

#ifndef
INLINER_FOR_JAVA

1989

|| (*lang_hooks

.tree_inlining.tree_chain_matters_p)
(*tp)

1990

|| STATEMENT_CODE_P (code))

1991

TREE_CHAIN (*tp) = chain;

1992

1993

/* For now, we don't update BLOCKs when we
make copies. So, we

1994

have
to nullify all scope-statements.
*/

1995

if (TREE_CODE (*tp) ==
SCOPE_STMT)

1996

SCOPE_STMT_BLOCK (*tp) =
NULL_TREE;

1997

#else

/* INLINER_FOR_JAVA */

1998

|| (*lang_hooks

.tree_inlining.tree_chain_matters_p)
(*tp))

1999

TREE_CHAIN (*tp) =
chain;

2000

#endif
/*
INLINER_FOR_JAVA */

2001

}

2002

else if (TREE_CODE_CLASS
(code) == 't')

2003

*walk_subtrees = 0;

2004

2005

return

NULL_TREE;

2006

}

一个
TARGET_EXPR
代表一个临时对象。其第一个操作数是表示这个临时对象的一个
VAR_DECL
。其第二个操作数是该临时对象的初始值。该初始值被评估,然后拷贝入这个临时对象。

一个
AGGR_INIT_EXPR
代表,作为一个函数调用返回值,或一个构造函数结果的初始化。一个
AGGR_INIT_EXPR
将仅出现作为一个
TARGET_EXPR
的第二个操作数。该
AGGR_INIT_EXPR
的第一个操作数是所调用函数的地址,就像在一个
CALL_EXPR
那样。第二个操作数是以一个
TREE_LIST
形式传递给该函数的实参,同样类似于在一个
CALL_EXPR
里那样。这个表达式的值由这个函数返回。

那么如果
AGGR_INIT_EXPR
被用于
TRAGET_EXPR
里,递归
break_out_target_exprs

来拷贝这个节点。对于这个拷贝过来的表达式,
build_cplus_new

为其初始化产生了代码。

2007

tree

2008

build_cplus_new

(tree type, tree init)
in
cp/tree.c

2009

{

2010

tree fn;

2011

tree slot;

2012

tree rval;

2013

int is_ctor;

2014

2015

/*
Make sure that we're not trying to create an instance of an

2016

abstract class.
*/

2017

abstract_virtuals_error
(NULL_TREE, type);

2018

2019

if (TREE_CODE (init) != CALL_EXPR &&
TREE_CODE (init) != AGGR_INIT_EXPR)

2020

return
convert (type, init);

2021

2022

fn = TREE_OPERAND (init, 0);

2023

is_ctor = (TREE_CODE (fn) ==
ADDR_EXPR

2024

&&
TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL

2025

&& DECL_CONSTRUCTOR_P
(TREE_OPERAND (fn, 0)));

2026

2027

slot = build_local_temp
(type);

2028

2029

/*
We split the CALL_EXPR into its function and its arguments here.

2030

Then,
in expand_expr, we put them back together. The reason for

2031

this
is that this expression might be a default argument

2032

expression. In that case, we need a new
temporary every time the

2033

expression is used. That's what
break_out_target_exprs does; it

2034

replaces every AGGR_INIT_EXPR with a copy
that uses a fresh

2035

temporary slot. Then, expand_expr builds up
a call-expression

2036

using
the new slot.
*/

2037

2038

/*
If we don't need to use a constructor to create an object of this

2039

type,
don't mess with AGGR_INIT_EXPR.
*/

2040

if (is_ctor ||
TREE_ADDRESSABLE (type))

2041

{

2042

rval = build
(AGGR_INIT_EXPR, type, fn, TREE_OPERAND (init, 1), slot);

2043

TREE_SIDE_EFFECTS (rval) =
1;

2044

AGGR_INIT_VIA_CTOR_P
(rval) = is_ctor;

2045

}

2046

else

2047

rval = init;

2048

2049

rval = build_target_expr
(slot, rval);

2050

2051

return
rval;

2052

}

现在需要更新在
TARGET_EXPR
中的临时对象,因为我们不是在产生原始的
TARGET_EXPR
的上下文中。看到这个临时对象成为了局部的,其
DECL_CONTEXT
被强制设置为
current_function_decl



253

static
tree

254

build_local_temp

(tree type)
in
cp/tree.c

255

{

256

tree slot = build_decl
(VAR_DECL, NULL_TREE, type);

257

DECL_ARTIFICIAL (slot) = 1;

258

DECL_CONTEXT (slot) = current_function_decl

;

259

layout_decl
(slot,
0);

260

return
slot;

261

}

注意如果对于这个
AGGR_INIT_EXPR

AGGR_INIT_VIA_CTOR_P
成立,表示这个初始化是通过一个构造函数调用实现。在
2042
行所构建的
AGGR_INIT_EXPR
,其第三个操作数是这个临时对象,它总是一个
VAR_DECL
。而
init

是原来
TARGET_EXPR
中的
AGGR_INIT_EXPR
(这个
AGGR_INIT_EXPR
的第三个操作数的地址被提取,并替换实参列表中的值。这个情况下,该表达式的值是提供给这个
AGGR_INIT_EXPR

VAR_DECL
;构造函数不返回值)。

最后,这个新构建的
TARGET_EXPR

build_cplus_new

返回。

而对于
TARGET_EXPR
的第二个操作数不是
AGGR_INIT_EXPR
的情况,该操作数由
build_target_expr_with_type

来处理。这里如果
init


TARGET_EXPR
,它必定是由在
bot_manip


1209
行的
break_out_target_exprs

所构建的,这个节点正是我们期望的。

320

tree

321

build_target_expr_with_type

(tree init,
tree type)
in
cp/tree.c

322

{

323

tree slot;

324

325

if (TREE_CODE (init) == TARGET_EXPR)

326

return
init;

327

else if (CLASS_TYPE_P (type) &&
!TYPE_HAS_TRIVIAL_INIT_REF (type)

328

&& TREE_CODE (init) !=
COND_EXPR

329

&& TREE_CODE (init) !=
CONSTRUCTOR

330

&& TREE_CODE (init) !=
VA_ARG_EXPR)

331

/* We need to
build up a copy constructor call. COND_EXPR is a special

332

case because we
already have copies on the arms and we don't want

333

another one
here. A CONSTRUCTOR is aggregate initialization, which

334

is handled
separately. A VA_ARG_EXPR is magic creation of an

335

aggregate; there's no additional work to be
done.
*/

336

return
force_rvalue
(init);

337

338

slot = build_local_temp
(type);

339

return
build_target_expr
(slot, init);

340

}

上面,如果
TYPE_HAS_TRIVIAL_INIT_REF
不是
0
,表示该拷贝初始化可以使用按位拷贝。对于这样的情形,可以简单地构建
TARGET_EXPR
;否则,需要执行一个左值到右值的转换,包括如下所示的拷贝构造函数调用。

590

tree

591

force_rvalue

(tree expr)
in
cvt.c

592

{

593

if (IS_AGGR_TYPE (TREE_TYPE
(expr)) && TREE_CODE (expr) != TARGET_EXPR)

594

expr = ocp_convert (TREE_TYPE (expr), expr,

595

CONV_IMPLICIT|CONV_FORCE_TEMP, LOOKUP_NORMAL);

596

else

597

expr = decay_conversion
(expr);

598

599

return
expr;

600

}

不久之后我们将看到
ocp_convert

的细节。在这里总而言之,该函数将产生调用合适的拷贝构造函数代码,然后调用
build_cplus_new

来产生构建这个临时对象及其初始化。在离开
bot_manip

之前,看一下
build_target_expr



234

static
tree

235

build_target_expr

(tree decl, tree
value)
in
cp/tree.c

236

{

237

tree t;

238

239

t = build
(TARGET_EXPR,
TREE_TYPE (decl), decl, value,

240

cxx_maybe_build_cleanup
(decl), NULL_TREE);

241

/* We always set
TREE_SIDE_EFFECTS so that expand_expr does not

242

ignore the
TARGET_EXPR. If there really turn out to be no

243

side-effects,
then the optimizer should be able to get rid of

244

whatever code is
generated anyhow.
*/

245

TREE_SIDE_EFFECTS (t) = 1;

246

247

return
t;

248

}

对于具有非平凡析构函数的临时对象,编译器需要产生代码,在其越出其作用域时,通过调用这个析构函数摧毁这个临时对象。因此在
240
行,
cxx_maybe_build_cleanup

产生这些代码,如果需要的话。

现在在
bot_manip


1213
行,
u

是对应于
t


TARGET_EXPR
。它把新旧版本的临时对象映射起来。然后,我们立即用新版本取代了旧版本。不过,在由
break_out_target_exprs

处理的节点的某些子节点中,可能仍然保留了这个旧版本的引用,它们需要如下的更新。

1236

static
tree

1237

bot_replace

(tree* t,
in
cp/tree.c

1238

int* walk_subtrees
ATTRIBUTE_UNUSED ,

1239

void* data)

1240

{

1241

splay_tree target_remap =
((splay_tree) data);

1242

1243

if (TREE_CODE (*t) ==
VAR_DECL)

1244

{

1245

splay_tree_node n =
splay_tree_lookup (target_remap,

1246

(splay_tree_key)
*t);

1247

if (n)

1248

*t = (tree) n->value;

1249

}

1250

1251

return
NULL_TREE;

1252

}

回到
convert_default_arg

,在
4274
行从
break_out_target_exprs

得到这个更新的
arg

,然后接下来的函数用于产生初始化代码。

5.12.5.2.2.2.3.

省略实参

最后一个的可能就是省略实参,注意到省略实参与缺省实参不可共存。在前端中,为了识别包含了省略实参的函数声明,形参列表由
NULL
,而不是
void_list_node

来结尾。

4161

tree

4162

convert_arg_to_ellipsis

(tree arg)
in
call.c

4163

{

4164

/*
[expr.call]

4165

4166

The
lvalue-to-rvalue, array-to-pointer, and function-to-pointer

4167

standard conversions are performed.
*/

4168

arg = decay_conversion
(arg);

4169

/* [expr.call]

4170

4171

If the
argument has integral or enumeration type that is subject

4172

to the
integral promotions (_conv.prom_), or a floating point

4173

type
that is subject to the floating point promotion

4174

(_conv.fpprom_), the value of the argument
is converted to the

4175

promoted type before the call.
*/

4176

if (TREE_CODE (TREE_TYPE
(arg)) == REAL_TYPE

4177

&&
(TYPE_PRECISION (TREE_TYPE (arg))

4178

< TYPE_PRECISION
(double_type_node)))

4179

arg = convert_to_real
(double_type_node, arg);

4180

else if (INTEGRAL_OR_ENUMERATION_TYPE_P
(TREE_TYPE (arg)))

4181

arg =
perform_integral_promotions (arg);

4182

4183

arg = require_complete_type
(arg);

4184

4185

if (arg != error_mark_node

4186

&& !pod_type_p
(TREE_TYPE (arg)))

4187

{

4188

/*
Undefined behavior [expr.call] 5.2.2/7. We used
to just warn

4189

here
and do a bitwise copy, but now cp_expr_size will abort if we

4190

try
to do that.

4191

If
the call appears in the context of a sizeof expression,

4192

there is no need to emit a warning, since
the expression won't be

4193

evaluated. We keep the builtin_trap just
as a safety check.
*/

4194

if (!skip_evaluation

)

4195

warning ("cannot
pass objects of non-POD type `%#T' through `...'; "

4196

"call will
abort at runtime", TREE_TYPE (arg));

4197

arg = call_builtin_trap
();

4198

arg = build
(COMPOUND_EXPR, integer_type_node, arg,

4199

integer_zero_node);

4200

}

4201

4202

return
arg;

4203

}


3
】的条文
5.2.2
“函数调用”,条款
7
如下定义了编译器在省略实参上的行为。

7.

当一个给定的实参没有对应的形参,该实参被以这样的方式传入——接受函数可以通过调用
va_arg

18.7
)来得到该实参值。左值到右值(
4.1
),数组到指针(
4.2
),及函数到指针(
4.3
)标准转换在该实参表达式上执行。在这些转换之后,如果该实参不是数字,枚举值,指针,成员的指针,或类类型,该程序非法。如果该实参具有一个
non-POD

类类型(条文
9
),其行为是未定义的。如果该实参具有适用于整型提升(
4.5
)的整型或枚举类型,或适用于浮点提升(
4.6
)的浮点类型,在该调用之前,该实参的值被转换到提升后的类型。这些提升被称为缺省实参提升


函数
decay_conversion

执行在
exp

中的转换,它应用在当一个左值出现在一个右值上下文中时候。包括左值到右值,数组到指针,及函数到指针的转换。

1335

tree

1336

decay_conversion

(tree exp)
n
typeck.c

1337

{

1338

tree type;

1339

enum
tree_code code;

1340

1341

type = TREE_TYPE (exp);

1342

code = TREE_CODE (type);

1343

1344

if (code == REFERENCE_TYPE)

1345

{

1346

exp = convert_from_reference
(exp);

1347

type = TREE_TYPE (exp);

1348

code = TREE_CODE (type);

1349

}

1350

1351

if (type == error_mark_node)

1352

return
error_mark_node;

1353

1354

if (type_unknown_p (exp))

1355

{

1356

cxx_incomplete_type_error
(exp, TREE_TYPE (exp));

1357

return
error_mark_node;

1358

}

1359

1360

/*
Constants can be used directly unless they're not loadable.
*/

1361

if (TREE_CODE (exp) ==
CONST_DECL)

1362

exp = DECL_INITIAL (exp);

1363

/*
Replace a nonvolatile const static variable with its value. We

1364

don't
do this for arrays, though; we want the address of the

1365

first
element of the array, not the address of the first element

1366

of its
initializing constant.
*/

1367

else if (code != ARRAY_TYPE)

1368

{

1369

exp = decl_constant_value
(exp);

1370

type = TREE_TYPE (exp);

1371

}

1372

1373

/* build_c_cast puts on a NOP_EXPR to make the
result not an lvalue.

1374

Leave
such NOP_EXPRs, since RHS is being used in non-lvalue context.
*/

1375

1376

if (code == VOID_TYPE)

1377

{

1378

error ("void value
not ignored as it ought to be");

1379

return
error_mark_node;

1380

}

1381

if
(invalid_nonstatic_memfn_p (exp))

1382

return
error_mark_node;

1383

if (code == FUNCTION_TYPE ||
is_overloaded_fn
(exp))

1384

return
build_unary_op (ADDR_EXPR, exp, 0);

1385

if (code == ARRAY_TYPE)

1386

{

1387

tree adr;

1388

tree ptrtype;

1389

1390

if (TREE_CODE (exp) ==
INDIRECT_REF)

1391

return
build_nop (build_pointer_type
(TREE_TYPE
(type)),

1392

TREE_OPERAND (exp, 0));

1393

1394

if (TREE_CODE (exp) ==
COMPOUND_EXPR)

1395

{

1396

tree op1 = decay_conversion
(TREE_OPERAND (exp, 1));

1397

return
build
(COMPOUND_EXPR, TREE_TYPE (op1),

1398

TREE_OPERAND
(exp, 0), op1);

1399

}

1400

1401

if (!lvalue_p (exp)

1402

&& ! (TREE_CODE
(exp) == CONSTRUCTOR && TREE_STATIC (exp)))

1403

{

1404

error ("invalid use
of non-lvalue array");

1405

return
error_mark_node;

1406

}

1407

1408

ptrtype = build_pointer_type
(TREE_TYPE (type));

1409

1410

if (TREE_CODE (exp) ==
VAR_DECL)

1411

{

1412

if
(!cxx_mark_addressable (exp))

1413

return
error_mark_node;

1414

adr = build_nop
(ptrtype, build_address
(exp));

1415

TREE_SIDE_EFFECTS (adr)
= 0;
/*
Default would be, same as EXP.
*/

1416

return
adr;

1417

}

1418

/*
This way is better for a COMPONENT_REF since it can

1419

simplify the offset for a component.
*/

1420

adr = build_unary_op
(ADDR_EXPR, exp, 1);

1421

return
cp_convert (ptrtype, adr);

1422

}

1423

1424

/*
[basic.lval]: Class rvalues can have cv-qualified types; non-class

1425

rvalues always have cv-unqualified
types.
*/

1426

if (! CLASS_TYPE_P (type))

1427

exp = cp_convert
(TYPE_MAIN_VARIANT (type), exp);

1428

1429

return
exp;

1430

}

首先对于
REFERENCE_TYPE
,它是一个左值,要把它转换为右值,就应该使用被引用的值,而不再保存引用。在前端中,
INDIRECT_REF
为这个目标而构建(这是因为引用总是传入其地址就像指针那样;而在编译器中,引用的模式(
mode
)与指针的相同,都是
ptr_mode

,参见
build_reference_type

)。

566

tree

567

convert_from_reference

(tree val)

in cvt.c

568

{

569

if (TREE_CODE (TREE_TYPE (val)) ==
REFERENCE_TYPE)

570

return
build_indirect_ref
(val, NULL);

571

return
val;

572

}

记得指针是右值。当处理
ARRAY_TYPE
时,如果我们正在使用形如:
int *a[i]

a
是维度大于
1
的数组,例如,
int
A[2][2]
)的表达式,满足
1390
行的条件,
因此构建了形如:
int**
的类型,并且看到转换指针“
int
*a[i]
”到“
int**
”不需要产生任何代码,因而为这个转换构建了
NOP_EXPR


而如果
exp

只是一个数组的声明,例如:
int a[8]
,这个声明的右值是“
int*
”。可以直接构建这个右值。不过,对于其他的情形,例如:“
tempA.a
”(这是一棵以
SCOPE_REF
为根节点的树),就没有可以直接使用的简单规则,因此调用
build_unary_op


cp_convert

来执行合适的转换。

正如在
convert_arg_to_ellipsis


4188
行的注释所提到的,对于对应于
non-POD
类类型的未定义行为,
GCC
以前使用按位拷贝,不过在当前版本中,这个行为在后面对
cp_expr_size

的调用将导致异常终止。这个终止由下面“
__builtin_trap
”的调用触发,在
4198
行它成为了
COMPOUND_EXPR
形式的
arg

的一部分。

4148

c
tree

4149

call_builtin_trap

(void)
in
call.c

4150

{

4151

tree fn = IDENTIFIER_GLOBAL_VALUE
(get_identifier ("__builtin_trap"));

4152

4153

my_friendly_assert (fn !=
NULL, 20030927);

4154

fn = build_call (fn,
NULL_TREE);

4155

return
fn;

4156

}

内建陷阱的行为是:如果目标机器定义了陷阱指令,就使用它;否则编译器将调用
abort

()
(参考
expand_builtin_trap

)。

下面是对
non-POD
类类型省略实参的一个有趣的测试:

#include
<stdarg.h>

class
A {

public
: virtual
void func() {}

};

int func (int
a, ...) {

va_list ap;

va_start(ap, a)
;

va_arg(ap, A);

va_end(ap);

return
1;

}

int main() {

A a;

func (1, a);
// sizeof (func (1, a))

}

编译器将给出以下警告:

test2.cpp: In function ‘int func(int, ...)’:

test2.cpp:11: warning: cannot receive objects of
non-POD type ‘class A’ through ‘...’; call will abort at runtime

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

test2.cpp:19: warning: cannot pass objects of non-POD
type ‘class A’ through ’...’; call will abort at runtime

当执行这个程序时,得到错误:
Illegal instruction.

不过,如果我们使用注释中的语句,编译器将给出警告:

test2.cpp: In function ‘int func(int, ...)’:

test2.cpp:11: warning: cannot receive objects of
non-POD type ‘class A’ through ‘...’; call will abort at runtime

而在执行时,没有产生错误,因为
func(1, a)
没有被评估,除了其返回值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: