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,constexprint,short)const,volatile)decltype)声明的第二部分是以逗号分隔的init-declarators.每个init-declarator都包含一系列声明符,可选地后跟一个初始化器.
什么声明者是:
iin int i;)*,&,&&,指针到构件语法)(int, char))[2][3])请注意,声明的结构是严格的:第一个说明符,然后是init声明符(每个声明符都可以后跟一个初始化器).
规则是:说明符适用于整个声明,而声明符仅适用于一个init-declarator(以逗号分隔列表的一个元素).
另请注意,cv-qualifier既可以用作说明符,也可以用作声明符.作为声明者,语法将它们限制为仅在指针存在时使用.
因此,要处理您发布的四个声明:
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".
int j = 0, const c = 2;
Run Code Online (Sandbox Code Playgroud)
同样,一个说明符:int和两个init-declarators:j = 0和const c = 2.
对于第二个init-declarator,声明符是const和c.正如我所提到的,如果涉及指针,语法只允许cv-qualifiers作为声明符.这不是这种情况,因此错误.
int *const p1 = nullptr, i1 = 0;
Run Code Online (Sandbox Code Playgroud)
一个说明符:int,两个init-declarators:* const p1 = nullptr和i1 = 0.
对于第一个初始化说明符的说明符:*,const,和p1.我们已经处理过这样的init-declarator(案例1中的第二个).它将"基本类型的常量指针"添加到指定符定义的基类型(仍然是int).
对于第二个init-declarator i1 = 0,很明显.没有类型修改,按原样使用说明符.所以i1成为一个int.
int const j1 = 0, c1 = 2;
Run Code Online (Sandbox Code Playgroud)
在这里,我们与前三个情况有着根本不同的情况.我们有两个说明符:int和const.然后是两个init-declarators,j1 = 0和c1 = 2.
这些init-declarators都没有任何类型修改声明符,所以它们都使用说明符中的类型,即const int.
这在[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-declarator与ptr-operator作为* const在这种情况下和ptr-declarator作为一个不合格的标识符.
您的第二个陈述不合法,因为它不是有效的ptr-operator:
const c = 2
Run Code Online (Sandbox Code Playgroud)
一个ptr-operator必须启动*,&,&&或嵌套名指定后面*.由于const c不是从这些令牌中的任何一个开始,我们认为const c是noptr-declarator,const这里不允许.
另外,为什么第3和第4陈述的行为不同?
因为int是type-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节"声明符".