简体   繁体   English

C ++中的声明

[英]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: 说明符是这样的:

  • storage class specifiers (eg static , extern ) 存储类说明符(例如staticextern
  • function specifiers (eg virtual , inline ) 函数说明符(例如virtualinline
  • friend , typedef , constexpr friendtypedefconstexpr
  • type specifiers , which include: 类型说明符 ,包括:
    • simple type specifiers (eg int , short ) 简单类型说明符(例如intshort
    • cv-qualifiers ( const , volatile ) cv-qualifiersconstvolatile
    • other things (eg 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: 什么声明者是:

  • identifier (eg the i in int i; ) 标识符(例如, iint i;
  • pointer-like operators ( * , & , && , pointer-to-member syntax) 类似指针的运算符( *&&& ,指向成员的指针语法)
  • function parameter syntax (eg (int, char) ) 函数参数语法(例如(int, char)
  • array syntax (eg [2][3] ) 数组语法(例如[2][3]
  • cv-qualifiers , if these follow a pointer declarator. cv-qualifiers ,如果这些遵循指针声明符。

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: 因此,要处理您发布的四个声明:

1 1

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有三个声明符: *constp 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常量指针”。

2 2

int j = 0, const c = 2;

Again, one specifier: int , and two init-declarators: j = 0 and const c = 2 . 同样,一个说明符: int和两个init-declarators: j = 0const c = 2

For the second init-declarator, the declarators are const and c . 对于第二个init-declarator,声明符是constc 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. 这不是这种情况,因此错误。

3 3

int *const p1 = nullptr, i1 = 0;

One specifier: int , two init-declarators: * const p1 = nullptr and i1 = 0 . 一个说明符: int ,两个init-declarators: * const p1 = nullptri1 = 0

For the first init-declarator, the declarators are: * , const , and p1 . 对于第一个init-declarator,声明符为: *constp1 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

4 4

int const j1 = 0, c1 = 2;

Here, we have a fundamentally different situation from the preceding three. 在这里,我们与前三个情况有着根本不同的情况。 We have two specifiers: int and const . 我们有两个说明符: intconst And then two init-declarators, j1 = 0 and c1 = 2 . 然后是两个init-declarators, j1 = 0c1 = 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-declaratorptr-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 , 因为inttype-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-specifierint (一个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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM