C++风格cast的优先级

一、为什么注意到这个问题

之前在使用C风格的cast时候,一个比较烦的地方就是由于转换操作的优先级,导致转换出来的类型需要使用额外的一个括号抱起来,看起来非常臃肿。后来注意到在C++风格的转换符貌似优先级更高,虽然输入量有所增加,但是输入的时候比较流畅,因此看起来也更加流程。
tsecer@harry: cat -n cpp.style.cast.cpp
1 struct S
2 {
3 int x;
4 };
5
6 int foo(void *vp)
7 {
8 return static_cast<const S*>(vp)->x
9 + (S*)(vp)->x;
10 }
tsecer@harry: g++ -c cpp.style.cast.cpp
cpp.style.cast.cpp: 在函数‘int foo(void*)’中:
cpp.style.cast.cpp:9:14: 错误:‘void*’不是一个指向对象的类型
+ (S*)(vp)->x;
^
tsecer@harry:

二、cast的优先级

在C语言中,明确定义了cast的优先级

优先级运算符描述结合性
1 :: 作用域解析 从左到右
2 a++   a-- 后缀自增与自减
type()   type{} 函数风格转型
a() 函数调用
a[] 下标
.   -> 成员访问
3 ++a   --a 前缀自增与自减 从右到左
+a   -a 一元加与减
!   ~ 逻辑非逐位非
(type) C 风格转型
*a 间接(解引用)
&a 取址
sizeof 取大小[注 1]
co_await await 表达式 (C++20)
new   new[] 动态内存分配
delete   delete[] 动态内存分配

 

它的优先级和“前缀递增”(++)相同,低于“成员访问”(->)的优先级。但是对于C++风格的转换,“注解”中的说明是
表中并未包括 const_cast、static_cast、dynamic_cast、reinterpret_cast、typeid、sizeof...、noexcept 及 alignof,因为它们决不会有歧义。
但是并没有明确说明它们的优先级。Stack Overflow上对该问题讨论的一个帖子

三、gcc中对该语法的处理

从该函数的实现可以看到,对于C++风格的cast,只要识别出 const_cast、static_cast、dynamic_cast、reinterpret_cast、这个关键字,后面的流程就是一马平川了,之后必须跟左尖括号/小于号(RT_LESS),类型(typeid),右尖括号/大于号(RT_GREATER)、左括弧(RT_OPEN_PAREN)、表达式(expression)、有括弧(RT_CLOSE_PAREN)。最关键的是,在这些解析完毕之后,直接调用build_dynamic_cast/build_static_cast/build_reinterpret_cast/build_const_cast来生成表达式,从这个意义上来说,C++风格的cast更像是一个原子操作,它们整个语法结构构成了一个基本单位
gcc-4.9.0\gcc\cp\parser.c
/* Parse a postfix-expression.

postfix-expression:
primary-expression
postfix-expression [ expression ]
postfix-expression ( expression-list [opt] )
simple-type-specifier ( expression-list [opt] )
typename :: [opt] nested-name-specifier identifier
( expression-list [opt] )
typename :: [opt] nested-name-specifier template [opt] template-id
( expression-list [opt] )
postfix-expression . template [opt] id-expression
postfix-expression -> template [opt] id-expression
postfix-expression . pseudo-destructor-name
postfix-expression -> pseudo-destructor-name
postfix-expression ++
postfix-expression --
dynamic_cast < type-id > ( expression )
static_cast < type-id > ( expression )
reinterpret_cast < type-id > ( expression )
const_cast < type-id > ( expression )
typeid ( expression )
typeid ( type-id )

GNU Extension:

postfix-expression:
( type-id ) { initializer-list , [opt] }

This extension is a GNU version of the C99 compound-literal
construct. (The C99 grammar uses `type-name' instead of `type-id',
but they are essentially the same concept.)

If ADDRESS_P is true, the postfix expression is the operand of the
`&' operator. CAST_P is true if this expression is the target of a
cast.

If MEMBER_ACCESS_ONLY_P, we only allow postfix expressions that are
class member access expressions [expr.ref].

Returns a representation of the expression. */

static tree
cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
bool member_access_only_p, bool decltype_p,
cp_id_kind * pidk_return)
{
cp_token *token;
location_t loc;
enum rid keyword;
cp_id_kind idk = CP_ID_KIND_NONE;
tree postfix_expression = NULL_TREE;
bool is_member_access = false;
int saved_in_statement = -1;

/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
loc = token->location;
/* Some of the productions are determined by keywords. */
keyword = token->keyword;
switch (keyword)
{
case RID_DYNCAST:
case RID_STATCAST:
case RID_REINTCAST:
case RID_CONSTCAST:
{
tree type;
tree expression;
const char *saved_message;
bool saved_in_type_id_in_expr_p;

/* All of these can be handled in the same way from the point
of view of parsing. Begin by consuming the token
identifying the cast. */
cp_lexer_consume_token (parser->lexer);

/* New types cannot be defined in the cast. */
saved_message = parser->type_definition_forbidden_message;
parser->type_definition_forbidden_message
= G_("types may not be defined in casts");

/* Look for the opening `<'. */
cp_parser_require (parser, CPP_LESS, RT_LESS);
/* Parse the type to which we are casting. */
saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
parser->in_type_id_in_expr_p = true;
type = cp_parser_type_id (parser);
parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
/* Look for the closing `>'. */
cp_parser_require (parser, CPP_GREATER, RT_GREATER);
/* Restore the old message. */
parser->type_definition_forbidden_message = saved_message;

bool saved_greater_than_is_operator_p
= parser->greater_than_is_operator_p;
parser->greater_than_is_operator_p = true;

/* And the expression which is being cast. */
cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
expression = cp_parser_expression (parser, /*cast_p=*/true, & idk);
cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);

parser->greater_than_is_operator_p
= saved_greater_than_is_operator_p;

/* Only type conversions to integral or enumeration types
can be used in constant-expressions. */
if (!cast_valid_in_integral_constant_expression_p (type)
&& cp_parser_non_integral_constant_expression (parser, NIC_CAST))
return error_mark_node;

switch (keyword)
{
case RID_DYNCAST:
postfix_expression
= build_dynamic_cast (type, expression, tf_warning_or_error);
break;
case RID_STATCAST:
postfix_expression
= build_static_cast (type, expression, tf_warning_or_error);
break;
case RID_REINTCAST:
postfix_expression
= build_reinterpret_cast (type, expression,
tf_warning_or_error);
break;
case RID_CONSTCAST:
postfix_expression
= build_const_cast (type, expression, tf_warning_or_error);
break;
default:
gcc_unreachable ();
}
}
break;
……
}

posted on 2020-03-20 16:50  tsecer  阅读(498)  评论(0编辑  收藏  举报

导航