Declarations in C ++

From what I understood, declarations / initializations in C ++ are “base type” statements, followed by a comma-separated list of declarators.

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 base type or a composite type?

From the error in the second declaration above, it seems basic. If so, what about the first declaration?

In other words, if the first statement is legal, why not the second? Also, why is the behavior different from the third and fourth statements?

+48
c ++ pointers language-lawyer declaration const
Jun 09 '16 at 9:53 on
source share
2 answers

Good question, with a complicated answer. To understand this, you need to fully understand the internal structure of C ++ declarations.

(Note that in this answer, I completely omit the existence of attributes to prevent overuse).

A declaration has two components: a sequence of qualifiers, followed by a comma-separated list of declarator initiators.

Qualifiers:

  • storage class specifiers (e.g. static , extern )
  • function specifiers (e.g. virtual , inline )
  • friend , typedef , constexpr
  • type specifiers that include:
    • simple type specifiers (e.g. int , short )
    • cv-qualifiers ( const , volatile )
    • other things (e.g. decltype )

The second part of the declaration is comma-separated init-declarators. Each init-declarator consists of a sequence of declarators, optionally followed by an initializer.

Which declarators:

  • (e.g. i in int i; )
  • operators like pointers ( * , & , && , element pointer syntax)
  • function parameter syntax (e.g. (int, char) )
  • array syntax (for example, [2][3] )
  • cv-qualifiers if they follow a pointer pointer.

Please note that the structure of the declaration is strict: first qualifiers, then init-declarators (each of which is a declarator, and then an initiator).

This rule: qualifiers apply to the entire declaration, and declarators apply to only one init-declarator (to one element of a comma-separated list).

Also note that the cv qualifier can be used both as a qualifier and as a declarator. As a declarator, grammar restricts their use only with pointers.

So, to process the four ads you posted:

one

 int i = 0, *const p = &i; 

The specifier part contains only one qualifier: int . This is the part to which all declarators will apply.

There are two init declarators: i = 0 and * const p = &i .

The first has one declarator i and initializer = 0 . Since there is no type modification declarator, type i is specified by int qualifiers in this case.

The second init-declarator has three declarations: * , const and p . And initializer = &i .

The declarers * and const change the base type to a "constant pointer to the base type". The base type specified by the qualifiers is int ; type p will be a "constant pointer to int ".

2

 int j = 0, const c = 2; 

Again, one qualifier: int and two init-declarators: j = 0 and const c = 2 .

For the second init-declarator, the declarators are const and c . As I mentioned, the grammar only allows cv-qualifiers as declarators, if there is a pointer. It is not, therefore, a mistake.

3

 int *const p1 = nullptr, i1 = 0; 

One specifier: int , two init-declarators: * const p1 = nullptr and i1 = 0 .

For the first init-declarator declarations: * , const and p1 . We have already considered such an init-declarator (the second in case 1 ). It adds a "constant pointer to the base type" to the specifier-defined base type (which is still int ).

For the second init-declarator i1 = 0 this is obvious. No type modifications, use specifiers (s) as is. So i1 becomes int .

four

 int const j1 = 0, c1 = 2; 

Here we have a fundamentally different situation from the previous three. We have two qualifiers: int and const . And then two init-declarators, j1 = 0 and c1 = 2 .

None of these initialization initiators have declarators that change the type in them, so they both use the type from the qualifiers, which is equal to const int .

+42
Jun 09 '16 at 10:38 on
source share
— -

This is indicated in [dcl.dcl] and [dcl.decl] as part of simple-declaration * and comes down to the differences between the branches in ptr-declarator :

 declaration-seq:
     declaration

 declaration:
     block-declaration

 block-declaration:
     simple-declaration

 simple-declaration:
     decl-specifier-seq opt init-declarator-list opt ;
 ----

 decl-specifier-seq:
     decl-specifier decl-specifier-seq    

 decl-specifier:    
     type-specifier & leftarrow;  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 initializer opt

 declarator:
     ptr-declarator

 ptr-declarator: & leftarrow;  here is the "switch"
     noptr-declarator
     ptr-operator ptr-declarator

 ptr-operator: & leftarrow;  allows const
     * cv-qualifier-seq opt

 cv-qualifier:
     const
     volatile

 noptr-declarator: & leftarrow;  does not allow const
     declarator-id

 declarator-id:
     id-expression

An important fork in the rules is in the ptr-declarator :

 ptr-declarator: noptr-declarator ptr-operator ptr-declarator 

Essentially, noptr-declarator in your context is just id-expression . It may not contain any cv-qualifier , but qualified or unqualified identifiers. However, a ptr-operator may contain cv-qualifier .

This indicates that your first statement works fine, since your second init-declarator

  *const p = &i; 

is a ptr-declarator form ptr-operator ptr-declarator , with ptr-operator being * const in this case, and ptr-declarator is an unqualified identifier.

Your second statement is not legal as it is not valid ptr-operator :

  const c = 2 

A ptr-operator must begin with * , & , && or a nested name specifier followed by * . Since const c does not start with any of these tokens, consider const c as a noptr-declarator , which does not allow const here.

Also, why is the behavior different from the 3rd and 4th operators?

Because int is type-specifier , and * is part of init-declarator ,

 *const p1 

declares a constant pointer.

However, in int const we have a decl-specifier-seq two decl-specifier , int (a simple-type-specifier ) and const (a cv-qualifier ), see trailing-type-specifier . Therefore, both form one declaration qualifier.




* Note. I missed all the alternatives that cannot be applied here and some rules have been simplified. For more details, see Section 7 "Declarations" and Section 8 "Declarators" C ++ 11 ( n3337 ).

+7
Jun 09 '16 at 10:48
source share



All Articles