const west oder east const?
Seit einigen Jahren tobt eine hitzige Debatte über diese Stilfrage auf den C++-Konferenzen, sogar mit Freundschafts-Armbändern! Für const-west-Anhänger sticht die Unveränderbarkeit als erstes beim Lesen heraus:
const char a = '-'; // oder char const b = '+'; // so?
Testfrage: Was ist bei p
und q
konstant?
const char *p = &a; // ?? char *const q = &a; // ??
Die simple Erklärung lautet: const
wirkt auf das unmittelbar links Davorstehende (außer wenn links nicht mehr steht, dann auf das unmittelbar rechts daneben). East-const-Verfechter brauchen die Ausnahme nicht zu erklären. Das ist für blutige Anfänger hilfreich:
char const *p = &a; // (veränderbarer) Zeiger auf konstantes Zeichen char *const q = &a; // konstanter Zeiger auf (veränderbares!) Zeichen
Die Definition für q
wird vom Compiler (zum Glück) nicht akzeptiert. Das hat aber nicht mit der east-west-Debatte zu tun, sondern mit der Verletzung eines Versprechens: Über den Komplizen q
wäre die Änderung von c möglich. Erlaubt ist
char const *const q = &a; // äquivalent zu const char *const q = &a;
Steigt man tiefer in C++ ein, kommen weitere Schlüsselwörter hinzu. Wohin kommen dann constexpr
, static
, inline
, … — wild-west, far-east?
Hintergrund
Um die Regeln zu ergründen, muss man die Grammatik von C++ heranziehen. [Dan Saks Video CppCon 2019]
Die Deklaration eines Namens besteht aus zwei Teilen. Der senkrechte Strich markiert die Grenze:
specifiers | declarator | static unsigned long int *name; \ \_________/ ^ \ wirkt auf / \____________________/
Specifiers dürfen in beliebiger Reihenfolge stehen. Davon gibt es zwei Sorten:
- type-specifiers (
int
long
unsigned
const
…) wirken sich auf den Typ aus. - non-type-specifiers (
static
thread_local
extern
mutable
inline
constexpr
…) wirken auf den definierten Namen: einestatic
-Variable existiert über die gesamte Programmdauer.
Im declarator können *
für Zeiger, []
für Felder und ()
für Funktionen und als Mittel zum Gruppieren vorkommen (in beliebig komplexen Kombinationen).
Dabei spielt const
(und volatile
) eine Doppelrolle. Es kann im specifier und auch im declarator hinter einem *
stehen. Das ist ein Versprechen, den Zeiger nicht zu ändern., bei mehrstufigen Zeigern den unmittelbar links davor stehenden. Darin liegt der Grund für die Anfängern leichter zugängliche east-const-Regel.
Dagegen darf constexpr
usw. nicht hinter dem *
auftauchen. Für diese Schlüsselwörter bleibt es eine Frage des Stils, in welcher Reihenfolge sie auftauchen: int long unsigned constexpr
ist unüblich…
Das Wichtigste ist, überhauptconst
hinzuschreiben.
Bildquelle:https://pbs.twimg.com/media/EEwwhRmX4AEs-DV?format=jpg&name=small