C++中的声明

sti*_*oob 48 c++ pointers const declaration language-lawyer

根据我的理解,C++中的声明/初始化是具有"基本类型"的语句,后跟逗号分隔的声明符列表.

请考虑以下声明:

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.
Run Code Online (Sandbox Code Playgroud)

const int基础类型还是复合类型?

从上面第二个声明中的错误看,它似乎是一个基类型.如果是这样,那么第一次宣言呢?

换句话说,如果第一个陈述是合法的,为什么不是第二个陈述?另外,为什么第三和第四个陈述的行为不同?

Rei*_*ica 42

好问题,答案很复杂.要真正掌握这一点,您需要非常彻底地了解C++声明的内部结构.

(注意,在这个答案中,我将完全省略属性的存在以防止过度复杂).

声明有两个组件:一系列说明符,后跟逗号分隔的init-declarators列表.

说明符是这样的:

  • 存储类说明符(例如static,extern)
  • 函数说明符(例如virtual,inline)
  • friend,typedef,constexpr
  • 类型说明符,包括:
    • 简单类型说明符(例如int,short)
    • cv-qualifiers(const,volatile)
    • 其他的事情(例如decltype)

声明的第二部分是以逗号分隔的init-declarators.每个init-declarator都包含一系列声明符,可选地后跟一个初始化器.

什么声明者是:

  • 标识符(例如iin int i;)
  • 指针状运算符(*,&,&&,指针到构件语法)
  • 函数参数语法(例如(int, char))
  • 数组语法(例如[2][3])
  • cv-qualifiers,如果它们遵循指针声明符.

请注意,声明的结构是严格的:第一个说明符,然后是init声明符(每个声明符都可以后跟一个初始化器).

规则是:说明符适用于整个声明,而声明符仅适用于一个init-declarator(以逗号分隔列表的一个元素).

另请注意,cv-qualifier既可以用作说明符,也可以用作声明符.作为声明者,语法将它们限制为仅在指针存在时使用.

因此,要处理您发布的四个声明:

1

int i = 0, *const p = &i;
Run Code Online (Sandbox Code Playgroud)

说明符部分只包含一个说明符:int.这是所有声明者将适用的部分.

有两个init-declarators:i = 0* const p = &i.

第一个有一个声明符i,和一个初始化器= 0.由于没有类型修改声明i符,因此int在这种情况下,由说明符给出类型.

第二初始化声明符有三个声明符:*,const,和p.和初始化,= &i.

声明符*const修改基类型以表示"指向基类型的常量指针".基类型,通过给定的说明符,是int,到的类型p将是"恒定的指针int".

2

int j = 0, const c = 2;
Run Code Online (Sandbox Code Playgroud)

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

对于第二个init-declarator,声明符是constc.正如我所提到的,如果涉及指针,语法只允许cv-qualifiers作为声明符.这不是这种情况,因此错误.

3

int *const p1 = nullptr, i1 = 0;
Run Code Online (Sandbox Code Playgroud)

一个说明符:int,两个init-declarators:* const p1 = nullptri1 = 0.

对于第一个初始化说明符的说明符:*,const,和p1.我们已经处理过这样的init-declarator(案例1中的第二个).它将"基本类型的常量指针"添加到指定符定义的基类型(仍然是int).

对于第二个init-declarator i1 = 0,很明显.没有类型修改,按原样使用说明符.所以i1成为一个int.

4

int const j1 = 0, c1 = 2;
Run Code Online (Sandbox Code Playgroud)

在这里,我们与前三个情况有着根本不同的情况.我们有两个说明符:intconst.然后是两个init-declarators,j1 = 0c1 = 2.

这些init-declarators都没有任何类型修改声明符,所以它们都使用说明符中的类型,即const int.


Zet*_*eta 7

这在[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

规则中的重要分支是ptr-declarator:

ptr-declarator:
    noptr-declarator
    ptr-operator ptr-declarator
Run Code Online (Sandbox Code Playgroud)

从本质上讲,noptr-declarator在您的背景下是id-expression唯一的.它可能不包含任何cv-qualifier但合格或不合格的ID.但是,一个ptr-operator可能包含一个cv-qualifier.

这表明您的第一个声明完全有效,因为您的第二个声明 init-declarator

 *const p = &i;
Run Code Online (Sandbox Code Playgroud)

ptr-declarator的形式ptr-operator ptr-declaratorptr-operator作为* const在这种情况下和ptr-declarator作为一个不合格的标识符.

您的第二个陈述不合法,因为它不是有效的ptr-operator:

 const c = 2
Run Code Online (Sandbox Code Playgroud)

一个ptr-operator必须启动*,&,&&或嵌套名指定后面*.由于const c不是从这些令牌中的任何一个开始,我们认为const cnoptr-declarator,const这里不允许.

另外,为什么第3和第4陈述的行为不同?

因为inttype-specifier,并且*init-declarator,

*const p1
Run Code Online (Sandbox Code Playgroud)

声明一个常量指针.

但是,int const我们有decl-specifier-seq两个decl-specifier,int(a simple-type-specifier)和const(a cv-qualifier),见trailing-type-specifier.因此,两者都形成一个声明说明符.


*注意:我省略了所有不能在这里应用的替代方案并简化了一些规则.有关更多信息,请参阅第7节"声明"和C++ 11(n3337)的第8节"声明符".