Deklaracje w C++

Z tego co zrozumiałem, deklaracje / inicjalizacje w C++ są deklaracjami z 'typem bazowym', po których następuje oddzielona przecinkami lista deklaratorów.

Rozważmy następujące deklaracje:

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.

Czy const int jest typem bazowym czy złożonym?

Z błędu w drugiej deklaracji powyżej wynika, że jest to typ bazowy. Jeśli tak, to co z pierwszą deklaracją?

Innymi słowy, jeśli pierwsze stwierdzenie jest legalne, dlaczego drugie nie? Również, dlaczego czy zachowanie różni się między trzecim i czwartym stwierdzeniem?
Author: Peter Mortensen, 2016-06-09

2 answers

Dobre pytanie, ze skomplikowaną odpowiedzią. Aby to naprawdę zrozumieć, musisz dość dokładnie zrozumieć wewnętrzną strukturę deklaracji C++.

(zauważ, że w tej odpowiedzi całkowicie pominę istnienie atrybutów, aby zapobiec nadmiernej komplikacji).

Deklaracja składa się z dwóch elementów: sekwencji specyfikacji, {[65] } , po której następuje oddzielona przecinkami lista INIT-deklaratorów.

Specyfikatorami są:

  • przechowywanie specyfikatory klas (np. static, extern)
  • specyfikatory funkcji (np. virtual, inline)
  • friend, typedef, constexpr
  • Typ specyfikacji , które obejmują:
    • specyfikatory prostych typów (np. int, short)
    • CV-kwalifikatory (const, volatile)
    • inne rzeczy (np. decltype)

Druga część deklaracji to oddzielone przecinkami deklaratory init. Każdy INIT-deklarator składa się z ciągu declarators, opcjonalnie następuje inicjalizator .

Czym są deklaracje:

  • identyfikator (np. i w int i;)
  • operatory podobne do wskaźników (*, &, &&, składnia pointer-to-member)
  • składnia parametru funkcji (np. (int, char))
  • składnia tablicy (np. [2][3])
  • CV-qualifiers , jeśli te następują po wskaźniku deklaratora.

Zauważ, że struktura deklaracji jest ścisła: najpierw specyfikatory, potem INIT-deklarators (każdy z nich jest deklarators opcjonalnie, a następnie inicjalizator).

Reguła jest następująca: specyfikatory mają zastosowanie do całej deklaracji, podczas gdy deklaratory mają zastosowanie tylko do jednego INIT-deklaratora (do jednego elementu listy rozdzielonej przecinkami).

Zwróć również uwagę powyżej, że kwalifikator cv może być używany zarówno jako specyfikator, jak i deklarator. Jako deklaratoria gramatyka ogranicza je do użycia tylko w obecności wskaźników.

Więc, aby obsłużyć cztery zamieszczone przez Ciebie deklaracje:

1

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

Część specifier zawiera tylko jeden specifier: int. Jest to część, do której będą stosować się wszyscy zgłaszający.

Istnieją dwa INIT-deklaratory: i = 0 i * const p = &i.

Pierwszy z nich ma jeden deklarator, i i inicjalizator = 0. Ponieważ nie ma deklaracji modyfikującej typ, typ i jest podawany przez specyfikatory, int w tym przypadku.

Drugi INIT-deklarator ma trzy zgłaszający / a: *, const, i p. I inicjalizatora, = &i.

Deklaratory * i const zmieniają typ bazowy na " stały wskaźnik do typu bazowego."Typ bazowy, podany przez specyfikatorów, to int, do typu p będzie" stałym wskaźnikiem do int."

2

int j = 0, const c = 2;

Ponownie jeden specyfikator: int i dwa INIT-deklaratory: j = 0 i const c = 2.

Dla drugiego INIT-declaratora, deklaraatorami są const i c. As I wspomniane, gramatyka dopuszcza CV-qualifiers jako deklaratory tylko wtedy, gdy występuje wskaźnik. Tutaj tak nie jest, stąd ten błąd.

3

int *const p1 = nullptr, i1 = 0;

Jeden specifier: int, dwa INIT-deklaratory: * const p1 = nullptr i i1 = 0.

Dla pierwszego INIT-declarator, deklaratory są: *, const, i p1. Mamy już do czynienia z takim INIT-deklarator (drugi w przypadku 1). Dodaje "stały wskaźnik do typu bazowego" do określonego typu bazowego (który jest nadal int).

Dla drugiego INIT-deklaratora i1 = 0, to oczywiste. Brak modyfikacji typu, użyj specyfikatorów w stanie, w jakim są. Więc i1 staje się int.

4

int const j1 = 0, c1 = 2;

Tutaj Mamy zasadniczo inną sytuację niż poprzednie trzy. Mamy dwa specyfikatory: int i const. A następnie dwa INIT-deklaratory, j1 = 0 i c1 = 2.

Żaden z tych INIT-deklaratorów nie ma w sobie żadnych deklaracji modyfikujących Typ, więc oba używają typu z specyfikatorów, czyli const int.

 42
Author: Angew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-09 10:44:27

Jest to określone w [dcl.dcl] i [dcl.decl] jako część simple-declaration* i sprowadza się do różnic między gałęziami w 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

Ważny widelec w regulaminie jest w ptr-declarator:

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

Zasadniczo, noptr-declarator w Twoim kontekście jest tylko id-expression. Nie może zawierać żadnych cv-qualifier, ale kwalifikowane lub niekwalifikowane identyfikatory. Jednak ptr-operator może zawierać cv-qualifier.

To wskazuje, że Twoje pierwsze stwierdzenie jest całkowicie poprawne, ponieważ twoje drugie init-declarator

 *const p = &i;

Jest ptr-declarator postaci ptr-operator ptr-declarator z ptr-operator będącą * const w tym przypadku i ptr-declarator będącą niekwalifikowanym identyfikatorem.

Twoje drugie oświadczenie nie jest legalne, ponieważ nie jest ważne ptr-operator:

 const c = 2

A ptr-operator musi zaczynać się od *, &, && lub zagnieżdżony specyfikator nazwy, po którym następuje *. Ponieważ const c nie zaczyna się od żadnego z tych tokenów, uznajemy const c za noptr-declarator, co nie pozwala const tutaj.

Również, dlaczego zachowanie / align = "left" /

Ponieważ int jest type-specifier, a * jest częścią init-declarator,

*const p1

Deklaruje stały wskaźnik.

Jednak w int const mamy decl-specifier-seq dwiedecl-specifier, int (a simple-type-specifier) i const (a cv-qualifier), zob. trailing-type-specifier. Dlatego oba tworzą jedną deklarację.


* Uwaga: pominąłem wszystkie alternatywy, których nie można tutaj zastosować i uprościłem niektóre zasady. Patrz sekcja 7 "deklaracje" oraz sekcja 8 "deklaracje" C++11 (n3337) aby uzyskać więcej informacji.

 7
Author: Zeta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-09 10:48:44