[英]Declarations in C++
From what I have understood, declarations/initializations in C++ are statements with 'base type' followed by a comma separated list of declarators. 根据我的理解,C ++中的声明/初始化是具有“基本类型”的语句,后跟逗号分隔的声明符列表。
Consider the following declarations: 请考虑以下声明:
int i = 0, *const p = &i; // Legal, the so-called base type is 'int'.
// i is an int while p is a const pointer to an int.
int j = 0, const c = 2; // Error: C++ requires a type specifier for all declarations.
// Intention was to declare j as an int and c an as const int.
int *const p1 = nullptr, i1 = 0; // p1 is a const pointer to an int while i1 is just an int.
int const j1 = 0, c1 = 2; // Both j1 and c1 are const int.
Is const int
a base type or a compound type? const int
是基类型还是复合类型?
From the error in the second declaration above, it seems to be a base type. 从上面第二个声明中的错误看,它似乎是一个基类型。 If it is so, then what about the first declaration? 如果是这样,那么第一次宣言呢?
In other words, if the first statement is legal, why isn't the second one? 换句话说,如果第一个陈述是合法的,为什么不是第二个陈述? Also, why does the behaviour differ among the third and fourth statements? 另外,为什么第三和第四个陈述的行为不同?
Good question, with a complicated answer. 好问题,答案很复杂。 To really grasp this, you need to understand the internal structure of C++ declarations quite thoroughly. 要真正掌握这一点,您需要非常彻底地理解C ++声明的内部结构。
(Note that in this answer, I will totally omit the existence of attributes to prevent overcomplication). (注意,在这个答案中,我将完全省略属性的存在以防止过度复杂)。
A declaration has two components: a sequence of specifiers, followed by a comma-separated list of init-declarators . 声明有两个组件:一系列说明符,后跟逗号分隔的init-declarators列表。
Specifiers are things like: 说明符是这样的:
static
, extern
) 存储类说明符(例如static
, extern
) virtual
, inline
) 函数说明符(例如virtual
, inline
) friend
, typedef
, constexpr
friend
, typedef
, constexpr
int
, short
) 简单类型说明符(例如int
, short
) const
, volatile
) cv-qualifiers ( const
, volatile
) decltype
) 其他的东西(例如decltype
) The second part of a declaration are the comma-separated init-declarators. 声明的第二部分是以逗号分隔的init-declarators。 Each init-declarator consists of a sequence of declarators, optionally followed by an initialiser. 每个init-declarator都包含一系列声明符,可选地后跟一个初始化器。
What declarators are: 什么声明者是:
i
in int i;
) 标识符(例如, i
在int i;
*
, &
, &&
, pointer-to-member syntax) 类似指针的运算符( *
, &
, &&
,指向成员的指针语法) (int, char)
) 函数参数语法(例如(int, char)
) [2][3]
) 数组语法(例如[2][3]
) Notice that the declaration's structure is strict: first specifiers, then init-declarators (each being declarators optionally followed by an initialiser). 请注意,声明的结构是严格的:第一个说明符,然后是init声明符(每个声明符都可以后跟一个初始化器)。
The rule is: specifiers apply to the entire declaration, while declarators apply only to the one init-declarator (to the one element of the comma-separated list). 规则是:说明符适用于整个声明,而声明符仅适用于一个init-declarator(以逗号分隔列表的一个元素)。
Also notice above that a cv-qualifier can be used as both a specifier and a declarator. 另请注意,cv-qualifier既可以用作说明符,也可以用作声明符。 As a declarator, the grammar restricts them to only be used in the presence of pointers. 作为声明者,语法将它们限制为仅在指针存在时使用。
So, to handle the four declarations you have posted: 因此,要处理您发布的四个声明:
int i = 0, *const p = &i;
The specifier part contains just one specifier: int
. 说明符部分只包含一个说明符: int
。 That is the part that all declarators will apply to. 这是所有声明者将适用的部分。
There are two init-declarators: i = 0
and * const p = &i
. 有两个init-declarators: i = 0
和* const p = &i
。
The first one has one declarator, i
, and an initialiser = 0
. 第一个有一个声明符i
,初始= 0
。 Since there is no type-modifying declarator, the type of i
is given by the specifiers, int
in this case. 由于没有类型修改声明符,因此i
的类型由说明符给出,在本例中为int
。
The second init-declarator has three declarators: *
, const
, and p
. 第二个init-declarator有三个声明符: *
, const
和p
。 And an initialiser, = &i
. 和初始化者, = &i
。
The declarators *
and const
modify the base type to mean "constant pointer to the base type." 声明符*
和const
修改基类型以表示“指向基类型的常量指针”。 The base type, given by specifiers, is int
, to the type of p
will be "constant pointer to int
." 由说明符给出的基类型是int
,对于p
的类型将是“指向int
常量指针”。
int j = 0, const c = 2;
Again, one specifier: int
, and two init-declarators: j = 0
and const c = 2
. 同样,一个说明符: int
和两个init-declarators: j = 0
和const c = 2
。
For the second init-declarator, the declarators are const
and c
. 对于第二个init-declarator,声明符是const
和c
。 As I mentioned, the grammar only allows cv-qualifiers as declarators if there is a pointer involved. 正如我所提到的,如果涉及指针,语法只允许cv-qualifiers作为声明符。 That is not the case here, hence the error. 这不是这种情况,因此错误。
int *const p1 = nullptr, i1 = 0;
One specifier: int
, two init-declarators: * const p1 = nullptr
and i1 = 0
. 一个说明符: int
,两个init-declarators: * const p1 = nullptr
和i1 = 0
。
For the first init-declarator, the declarators are: *
, const
, and p1
. 对于第一个init-declarator,声明符为: *
, const
和p1
。 We already dealt with such an init-declarator (the second one in case 1 ). 我们已经处理过这样的init-declarator(案例1中的第二个)。 It adds the "constant pointer to base type" to the specifier-defined base type (which is still int
). 它将“基本类型的常量指针”添加到说明符定义的基类型(仍为int
)。
For the second init-declarator i1 = 0
, it's obvious. 对于第二个init-declarator i1 = 0
,很明显。 No type modifications, use the specifier(s) as-is. 没有类型修改,按原样使用说明符。 So i1
becomes an int
. 所以i1
变成了一个int
。
int const j1 = 0, c1 = 2;
Here, we have a fundamentally different situation from the preceding three. 在这里,我们与前三个情况有着根本不同的情况。 We have two specifiers: int
and const
. 我们有两个说明符: int
和const
。 And then two init-declarators, j1 = 0
and c1 = 2
. 然后是两个init-declarators, j1 = 0
, c1 = 2
。
None of these init-declarators have any type-modifying declarators in them, so they both use the type from the specifiers, which is const int
. 这些init-declarators中没有一个在其中有任何类型修改声明符,因此它们都使用说明符中的类型,即const int
。
This is specified in [dcl.dcl] and [dcl.decl] as part of the simple-declaration
* and boils down to differences between the branches in ptr-declarator
: 这在[dcl.dcl]和[dcl.decl]中指定为simple-declaration
*的一部分,归结为ptr-declarator
分支之间的差异:
declaration-seq: declaration declaration: block-declaration block-declaration: simple-declaration simple-declaration: decl-specifier-seqopt init-declarator-listopt ; ---- decl-specifier-seq: decl-specifier decl-specifier-seq decl-specifier: type-specifier ← mentioned in your error type-specifier: trailing-type-specifier trailing-type-specifier: simple-type-specifier cv-qualifier ---- init-declarator-list: init-declarator init-declarator-list , init-declarator init-declarator: declarator initializeropt declarator: ptr-declarator ptr-declarator: ← here is the "switch" noptr-declarator ptr-operator ptr-declarator ptr-operator: ← allows const * cv-qualifier-seq opt cv-qualifier: const volatile noptr-declarator: ← does not allow const declarator-id declarator-id: id-expression
The important fork in the rules is in ptr-declarator
: 规则中的重要分支是在ptr-declarator
:
ptr-declarator:
noptr-declarator
ptr-operator ptr-declarator
Essentially, noptr-declarator
in your context is an id-expression
only. 实质上,上下文中的noptr-declarator
只是一个id-expression
。 It may not contain any cv-qualifier
, but qualified or unqualified ids. 它可能不包含任何cv-qualifier
,但不包含合格或不合格的id。 However, a ptr-operator
may contain a cv-qualifier
. 但是, ptr-operator
可能包含cv-qualifier
。
This indicates that your first statement is perfectly valid, since your second init-declarator
这表明您的第一个语句完全有效,因为您的第二个init-declarator
*const p = &i;
is a ptr-declarator
of form ptr-operator ptr-declarator
with ptr-operator
being * const
in this case and ptr-declarator
being a unqualified identifier. 是一个ptr-declarator
形式的ptr-operator ptr-declarator
与ptr-operator
感* const
在这种情况下和ptr-declarator
是一个不合格的标识符。
Your second statement isn't legal because it is not a valid ptr-operator
: 您的第二个陈述不合法,因为它不是有效的ptr-operator
:
const c = 2
A ptr-operator
must start with *
, &
, &&
or a nested name specifier followed by *
. ptr-operator
必须以*
, &
, &&
或嵌套的名称说明符开头,后跟*
。 Since const c
does not start with either of those tokens, we consider const c
as noptr-declarator
, which does not allow const
here. 由于const c
不是从这些令牌中的任何一个开始,我们将const c
视为noptr-declarator
,这里不允许使用const
。
Also, why the behaviour differs among 3rd and 4th statements? 另外,为什么第3和第4陈述的行为不同?
Because int
is the type-specifier
, and the *
is part of the init-declarator
, 因为int
是type-specifier
,而*
是init-declarator
,
*const p1
declares a constant pointer. 声明一个常量指针。
However, in int const
, we have a decl-specifier-seq
of two decl-specifier
, int
(a simple-type-specifier
) and const
(a cv-qualifier
), see trailing-type-specifier
. 但是,在int const
,我们有一个decl-specifier-seq
,它有两个decl-specifier
, int
(一个simple-type-specifier
)和const
(一个cv-qualifier
),参见trailing-type-specifier
。 Therefore both form one declaration specifier. 因此,两者都形成一个声明说明符。
* Note: I've omitted all alternatives which cannot be applied here and simplified some rules. *注意:我省略了所有不能在这里应用的替代方案并简化了一些规则。 Refer to section 7 "Declarations" and section 8 "Declarators" of C++11 ( n3337 ) for more information. 有关更多信息,请参阅第7节“声明”和C ++ 11( n3337 )的第8节“声明符” 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.