Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

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?

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?

like image 583
stillanoob Avatar asked Jun 09 '16 09:06

stillanoob


People also ask

What is declaration in C with example?

Declaration of a function provides the compiler with the name of the function, the number and type of arguments it takes, and its return type. For example, consider the following code, int add(int, int); Here, a function named add is declared with 2 arguments of type int and return type int.

What are declarations in programming?

In computer programming, a declaration is a language construct specifying identifier properties: it declares a word's (identifier's) meaning. Declarations are most commonly used for functions, variables, constants, and classes, but can also be used for other entities such as enumerations and type definitions.

What is declaration and example?

Word forms: plural declarations. 1. countable noun. A declaration is an official announcement or statement. They will sign the declaration tomorrow.

What are data declarations?

A declaration establishes the names and characteristics of data objects used in a program. A definition allocates storage for data objects, and associates an identifier with that object. When you declare or define a type, no storage is allocated.


2 Answers

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

(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.

Specifiers are things like:

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

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

What declarators are:

  • identifier (e.g. the i in int i;)
  • pointer-like operators (*, &, &&, pointer-to-member syntax)
  • function parameter syntax (e.g. (int, char))
  • array syntax (e.g. [2][3])
  • cv-qualifiers, if these follow a pointer declarator.

Notice that the declaration's structure is strict: first specifiers, then init-declarators (each being declarators optionally followed by an initialiser).

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).

Also notice above that a cv-qualifier can be used as both a specifier and a declarator. 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

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

The specifier part contains just one specifier: int. That is the part that all declarators will apply to.

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

The first one has one declarator, i, and an initialiser = 0. Since there is no type-modifying declarator, the type of i is given by the specifiers, int in this case.

The second init-declarator has three declarators: *, const, and p. And an initialiser, = &i.

The declarators * and const modify the base type to mean "constant pointer to the base type." The base type, given by specifiers, is int, to the type of p will be "constant pointer to int."

2

int j = 0, const c = 2; 

Again, one specifier: 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 involved. That is not the case here, hence the error.

3

int *const p1 = nullptr, i1 = 0; 

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

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

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

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. And then two init-declarators, j1 = 0 and 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.

like image 156
Angew is no longer proud of SO Avatar answered Sep 24 '22 13:09

Angew is no longer proud of SO


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:

 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:     noptr-declarator     ptr-operator ptr-declarator 

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

This indicates that your first statement is perfectly valid, since your second 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.

Your second statement isn't legal because it is not a valid ptr-operator:

 const c = 2 

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

Also, why the behaviour differs among 3rd and 4th statements?

Because int is the type-specifier, and the * is part of the 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. 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.

like image 33
Zeta Avatar answered Sep 23 '22 13:09

Zeta