Deklarationen
Deklarationen führen Namen in C++-Programmen ein (oder führen sie erneut ein). Nicht alle Deklarationen deklarieren tatsächlich etwas, und jede Art von Entität wird unterschiedlich deklariert. Definitionen sind Deklarationen, die ausreichen, um die durch den Namen identifizierte Entität zu verwenden.
Eine Deklaration ist eine der folgenden
- Funktionsdefinition
- Templatedeklaration (einschließlich partieller Templatespezialisierung)
- Explizite Template-Instanziierung
- Explizite Templatespezialisierung
- Namespace-Definition
- Linkage-Spezifikation
|
(seit C++11) |
- Leere Deklaration (
;) - Eine Funktionsdeklaration ohne decl-specifier-seq
attr (optional) deklarator ; |
|||||||||
| attr | - | (seit C++11) Sequenz einer beliebigen Anzahl von Attributen |
| Deklarator | - | ein Funktionsdeklarator |
- Diese Deklaration muss einen Konstruktor, einen Destruktor oder eine benutzerdefinierte Typ-Konstruktorfunktion Konstruktorfunktion deklarieren. Sie kann nur als Teil einer Templatedeklaration, einer expliziten Spezialisierung oder einer expliziten Instanziierung verwendet werden.
- block-deklaration (eine Deklaration, die innerhalb eines Blocks erscheinen kann), die wiederum eine der folgenden sein kann
| (seit C++11) |
| (seit C++20) | |
|
(seit C++11) |
- Einfache Deklaration
Inhalt |
[bearbeiten] Einfache Deklaration
Eine einfache Deklaration ist eine Anweisung, die einen oder mehrere Bezeichner einführt, erstellt und optional initialisiert, typischerweise Variablen.
decl-specifier-seq init-declarator-list (optional) ; |
(1) | ||||||||
attr decl-specifier-seq init-declarator-list ; |
(2) | (seit C++11) | |||||||
| decl-specifier-seq | - | Sequenz von Spezifizierern |
| init-declarator-list | - | durch Kommas getrennte Liste von init-declarator (siehe unten) |
| attr | - | Sequenz einer beliebigen Anzahl von Attributen |
init-declarator-list kann nur beim Deklarieren einer benannten Klasse oder einer benannten Aufzählung weggelassen werden.
|
Eine strukturierte Bindungsdeklaration ist ebenfalls eine einfache Deklaration. |
(seit C++17) |
Die Syntax von init-declarator ist wie folgt definiert
| deklarator initializer | (1) | ||||||||
| deklarator requires-clause (optional) contract-specs (optional) | (2) | ||||||||
| Deklarator | - | ein Deklarator |
| Initialisierer | - | ein Initialisierer |
| requires-clause | - | (seit C++20) eine requires-Klausel |
| contract-specs | - | (seit C++26) eine Liste von Funktionsvertragspezifizierern |
|
requires-clause kann nur erscheinen, wenn deklarator eine Templatfunktion deklariert. |
(seit C++20) |
|
contract-specs kann nur erscheinen, wenn deklarator eine Funktion oder eine Funktionstemplate deklariert. |
(seit C++26) |
[bearbeiten] Spezifizierer
Deklarationsspezifizierer (decl-specifier-seq) ist eine Sequenz der folgenden Leerzeichen-getrennten Spezifizierer in beliebiger Reihenfolge
- der
typedef-Spezifizierer. Wenn vorhanden, ist die gesamte Deklaration eine typedef-Deklaration und jeder Deklarator führt einen neuen Typnamen ein, kein Objekt oder eine Funktion. - Funktionsspezifizierer (
inline,virtual,explicit), nur zulässig in Funktionsdeklarationen.
|
(seit C++17) |
- Der
friend-Spezifizierer, zulässig in Klassen- und Funktionsdeklarationen.
|
(seit C++11) |
|
(seit C++20) |
- Speicherklassen-Spezifizierer (register, (bis C++17) static, thread_local, (seit C++11) extern, mutable). Nur ein Speicherklassen-Spezifizierer ist zulässig, außer dass thread_local zusammen mit extern oder static erscheinen darf(seit C++11).
- Typ-Spezifizierer (type-specifier-seq), eine Sequenz von Spezifizierern, die einen Typ benennen. Der Typ jeder durch die Deklaration eingeführten Entität ist dieser Typ, optional modifiziert durch den Deklarator (siehe unten). Diese Sequenz von Spezifizierern wird auch von Typ-IDs verwendet. Nur die folgenden Spezifizierer sind Teil von type-specifier-seq, in beliebiger Reihenfolge
- Klassenspezifizierer
- Enum-Spezifizierer
- einfacher Typ-Spezifizierer
| (seit C++11) | |
| (seit C++26) |
- zuvor deklarierter Klassenname (optional qualifiziert)
- zuvor deklarierter Enum-Name (optional qualifiziert)
- zuvor deklarierter typedef-Name oder Typ-Alias(seit C++11) (optional qualifiziert)
- Template-Name mit Template-Argumenten (optional qualifiziert, optional unter Verwendung des Template-Disambiguators)
|
(seit C++17) |
-
- das Schlüsselwort class, struct oder union, gefolgt von dem Bezeichner (optional qualifiziert), der zuvor als Name einer Klasse definiert wurde.
- das Schlüsselwort class, struct oder union, gefolgt von einem Template-Namen mit Template-Argumenten (optional qualifiziert, optional unter Verwendung des template-Disambiguators), der zuvor als Name eines Klassentemplates definiert wurde.
- das Schlüsselwort enum gefolgt von dem Bezeichner (optional qualifiziert), der zuvor als Name einer Aufzählung deklariert wurde.
- nur ein Typ-Spezifizierer ist in einem decl-specifier-seq zulässig, mit den folgenden Ausnahmen
- const kann mit jedem Typ-Spezifizierer kombiniert werden, außer mit sich selbst.
- volatile kann mit jedem Typ-Spezifizierer kombiniert werden, außer mit sich selbst.
- signed oder unsigned kann mit char, long, short oder int kombiniert werden.
- short oder long kann mit int kombiniert werden.
- long kann mit double kombiniert werden.
|
(seit C++11) |
Attribute können in decl-specifier-seq erscheinen, in diesem Fall gelten sie für den durch die vorhergehenden Spezifizierer bestimmten Typ.
Wiederholungen von Spezifizierern in einem decl-specifier-seq, wie const static const oder virtual inline virtual sind Fehler, mit der Ausnahme, dass long zweimal erscheinen darf(seit C++11).
[bearbeiten] Deklaratoren
Jeder init-declarator in einer init-declarator-list S D1, D2, D3; wird so verarbeitet, als wäre er eine eigenständige Deklaration mit denselben Spezifizierern: S D1; S D2; S D3;.
Jeder Deklarator führt genau ein Objekt, eine Referenz, eine Funktion oder (bei typedef-Deklarationen) einen Typ-Alias ein, dessen Typ durch decl-specifier-seq bereitgestellt und optional durch Operatoren wie & (Referenz auf), [] (Array von) oder () (Funktion, die zurückgibt) im Deklarator modifiziert wird. Diese Operatoren können rekursiv angewendet werden, wie unten gezeigt.
Ein deklarator ist einer der folgenden
| unqualified-id attr (optional) | (1) | ||||||||
| qualified-id attr (optional) | (2) | ||||||||
... identifier attr (optional) |
(3) | (seit C++11) | |||||||
* attr (optional) cv (optional) deklarator |
(4) | ||||||||
nested-name-specifier * attr (optional) cv (optional) deklarator |
(5) | ||||||||
& attr (optional) deklarator |
(6) | ||||||||
&& attr (optional) deklarator |
(7) | (seit C++11) | |||||||
noptr-declarator [ constexpr (optional) ] attr (optional) |
(8) | ||||||||
noptr-declarator ( parameter-list ) cv (optional) ref (optional) except (optional) attr (optional) |
(9) | ||||||||
D als Zeiger auf den Typ, der durch decl-specifier-seq S bestimmt wird.D als Zeiger auf ein Mitglied von C vom Typ, der durch decl-specifier-seq S bestimmt wird. nested-name-specifier ist eine Sequenz von Namen und Bereichsauflösungsoperatoren ::D als lvalue-Referenz auf den Typ, der durch decl-specifier-seq S bestimmt wird.D als rvalue-Referenz auf den Typ, der durch decl-specifier-seq S bestimmt wird.|
In allen Fällen ist attr eine optionale Sequenz von Attributen. Wenn sie unmittelbar nach dem Bezeichner erscheint, gilt sie für das zu deklarierende Objekt. |
(seit C++11) |
cv ist eine Sequenz von const- und volatile-Qualifizierern, wobei jeder Qualifizierer höchstens einmal in der Sequenz erscheinen darf.
| Dieser Abschnitt ist unvollständig Grund: Regeln für die Benennungsverschleierung von Deklarationen erklären; wie eine Variablen-/Funktionsdeklaration eine Klasse (aber kein typedef) mit demselben Namen verschleiert |
[bearbeiten] Hinweise
Wenn eine block-deklaration innerhalb eines Blocks erscheint und ein durch eine Deklaration eingeführter Bezeichner zuvor in einem äußeren Block deklariert wurde, wird die äußere Deklaration für den Rest des Blocks versteckt.
Wenn eine Deklaration eine Variable mit automatischer Speicherauer einführt, wird sie initialisiert, wenn ihre Deklarationsanweisung ausgeführt wird. Alle automatischen Variablen, die in einem Block deklariert werden, werden beim Verlassen des Blocks zerstört (unabhängig davon, wie der Block verlassen wird: durch Ausnahme, goto oder durch Erreichen seines Endes), in umgekehrter Reihenfolge ihrer Initialisierung.
[bearbeiten] Beispiel
Hinweis: Dieses Beispiel zeigt, wie einige komplexe Deklarationen gemäß der Grammatik der Sprache interpretiert werden. Andere gängige Eselsbrücken sind: die Spiralregel, das Lesen von innen nach außen und Deklaration spiegelt Verwendung. Es gibt auch einen automatisierten Parser unter https://cdecl.org.
#include <type_traits> struct S { int member; // decl-specifier-seq is "int" // declarator is "member" } obj, *pObj(&obj); // decl-specifier-seq is "struct S { int member; }" // declarator "obj" declares an object of type S // declarator "*pObj" declares a pointer to S, // and initializer "(&obj)" initializes it int i = 1, *p = nullptr, f(), (*pf)(double); // decl-specifier-seq is "int" // declarator "i" declares a variable of type int, // and initializer "= 1" initializes it // declarator "*p" declares a variable of type int*, // and initializer "= nullptr" initializes it // declarator "f()" declares (but doesn't define) // a function taking no arguments and returning int // declarator "(*pf)(double)" declares a pointer to function // taking double and returning int int (*(*var1)(double))[3] = nullptr; // decl-specifier-seq is "int" // declarator is "(*(*var1)(double))[3]" // initializer is "= nullptr" // 1. declarator "(*(*var1)(double))[3]" is an array declarator: // Type declared is: "(*(*var1)(double))" array of 3 elements // 2. declarator "(*(*var1)(double))" is a pointer declarator: // Type declared is: "(*var1)(double)" pointer to array of 3 elements // 3. declarator "(*var1)(double)" is a function declarator: // Type declared is: "(*var1)" function taking "(double)", // returning pointer to array of 3 elements. // 4. declarator "(*var1)" is a pointer declarator: // Type declared is: "var1" pointer to function taking "(double)", // returning pointer to array of 3 elements. // 5. declarator "var1" is an identifier. // This declaration declares the object var1 of type "pointer to function // taking double and returning pointer to array of 3 elements of type int" // The initializer "= nullptr" provides the initial value of this pointer. // C++11 alternative syntax: auto (*var2)(double) -> int (*)[3] = nullptr; // decl-specifier-seq is "auto" // declarator is "(*var2)(double) -> int (*)[3]" // initializer is "= nullptr" // 1. declarator "(*var2)(double) -> int (*)[3]" is a function declarator: // Type declared is: "(*var2)" function taking "(double)", returning "int (*)[3]" // ... int main() { static_assert(std::is_same_v<decltype(var1), decltype(var2)>); }
[bearbeiten] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 482 | C++98 | die Deklaratoren von Neu-Deklarationen konnten nicht qualifiziert werden | qualifizierte Deklaratoren erlaubt |
| CWG 569 | C++98 | ein einzelnes Semikolon war keine gültige Deklaration | es ist eine leere Deklaration, die keine Auswirkung hat |
| CWG 1830 | C++98 | Wiederholung eines Funktionsspezifizierers in einem decl-specifier-seq war erlaubt | Wiederholung ist verboten |
[bearbeiten] Siehe auch
| C-Dokumentation für Deklarationen
|