Warum können wir in C ++ den Variablennamen beim Deklarieren einer Variablen in Klammern setzen?

82

Zum Beispiel eine Erklärung wie die folgende:

int (x) = 0;

Oder sogar das:

int (((x))) = 0;

Ich bin darauf gestoßen, weil ich in meinem Code zufällig ein Fragment hatte, das dem folgenden ähnlich ist:

struct B
{
};

struct C
{
  C (B *) {}
  void f () {};
};

int main()
{
  B *y;
  C (y);
}

Offensichtlich wollte ich ein Objekt konstruieren, Cdas dann etwas Nützliches in seinem Destruktor tun würde. In diesem Fall wird der Compiler jedoch C (y);als Deklaration einer Variablen ymit Typ behandelt Cund gibt daher einen Fehler bezüglich der yNeudefinition aus. Interessant ist, dass wenn ich es so C (y).f ()oder so schreibe, es C (static_cast<B*> (y))wie beabsichtigt kompiliert wird. Die beste moderne Problemumgehung ist {}natürlich die Verwendung im Konstruktoraufruf.

Wie ich später herausgefunden habe, ist es möglich, Variablen wie int (x) = 0;oder sogar zu deklarieren, int (((x))) = 0;aber ich habe noch nie jemanden gesehen, der solche Deklarationen tatsächlich verwendet. Ich bin also interessiert - was ist der Zweck einer solchen Möglichkeit, weil ich im Moment sehe, dass sie nur den Fall schafft, der der berüchtigten "ärgerlichsten Analyse" ähnelt und nichts Nützliches hinzufügt?

Predelnik
quelle
Der "Zweck" der Möglichkeit besteht wahrscheinlich darin, den Parser zu vereinfachen.
Molbdnilo
1
@GSerg Lustig, wie der Text meiner Frage auf die Frage aus der zweiten Antwort in Ihrer verknüpften Frage antwortet, da ich das Beispiel
gebe,
ging in diese Falle: wusste nicht, dass Mutex auskommentiert wurde und schrieb dann versehentlich die falsche Klammerdeklaration. // std :: mutex std :: unique_lock <std :: mutex> (m_mutex);
Serve Laurijssen

Antworten:

80

Gruppierung.

Beachten Sie als besonderes Beispiel, dass Sie eine Variable vom Funktionstyp deklarieren können, z

int f(int);

Wie würden Sie nun einen Zeiger auf so etwas deklarieren?

int *f(int);

Nein, funktioniert nicht! Dies wird als Rückgabe einer Funktion interpretiert int*. Sie müssen die Klammern hinzufügen, damit sie richtig analysiert werden:

int (*f)(int);

Das gleiche gilt für Arrays:

int *x[5];   // array of five int*
int (*x)[5]; // pointer to array of five int

quelle
12
Und um die Antwort zu vervollständigen: Um den speziellen Fall, nach dem der Fragesteller fragt, nicht zuzulassen, wäre eine zusätzliche Sonderfallregel erforderlich. Die aktuelle Definition, wie ()in einem Typ gearbeitet wird, ist im gesamten Typ einheitlich.
Joseph Mansfield
Der Sonderfall gilt also für die ärgerlichste Analyse. Dies liegt daran, dass die Syntax zum Initialisieren von Variablen mit Konstruktorargumenten später hinzugefügt wurde (in Eile, denke ich?).
AnArrayOfFunctions
1
@ FISOCPP Nun. . Ja. C ++ kam nach C .. .
Iheanyi
Gilt diese Antwort auch für C, nicht nur für C ++?
Kdbanman
" Variable vom Funktionstyp " was? !!
Neugieriger
17

In solchen Deklarationen dürfen im Allgemeinen Klammern verwendet werden, da die Deklaration aus syntaktischer Sicht immer so aussieht:

<front type> <specification>;

Zum Beispiel in der folgenden Erklärung:

int* p[2];

Der "Fronttyp" ist int(nicht int*) und die "Spezifikation" ist * p[2].

Die Regel ist, dass Sie im Teil "Spezifikation" eine beliebige Anzahl von Klammern verwenden können, da diese manchmal nicht eindeutig zu unterscheiden sind. Beispielsweise:

int* p[2]; // array of 2 pointers to int; same as int (*p[2]);
int (*p)[2]; // pointer to an array of 2 ints

Der Zeiger auf ein Array ist ein seltener Fall, jedoch die gleiche Situation wie bei einem Zeiger auf die Funktion:

int (*func(int)); // declares a function returning int*
int (*func)(int); // declares a pointer to function returning int

Dies ist die direkte Antwort auf Ihre Frage. Wenn Ihre Frage über die Aussage wie ist C(y), dann:

  • Setzen Sie den gesamten Ausdruck in Klammern - (C(y))und Sie erhalten, was Sie wollten
  • Diese Aussage macht nichts anderes, als ein temporäres Objekt zu erstellen, das nach dem Ende dieser Anweisung nicht mehr lebt (ich hoffe, das ist, was Sie beabsichtigt haben).
Ethouris
quelle
1
Wie ich bereits erwähnt habe, war es anfangs etwas im Destruktor. Ich denke, es ist ziemlich normal, wenn Sie eine Reihe von "Verkettungs" -Funktionen haben, um einige Parameter einzustellen und dann den gesamten Job im Destruktor zu erledigen. Vielen Dank für eine weitere Problemumgehung. Ich denke, das Schreiben {}ist doch die gültigste.
Predelnik
4
Vermeiden Sie es, Ihre eigene Grammatik zu erstellen, und verwenden Sie die im Standard angegebene. <front-type> <specification>ist irreführend und falsch. Die Grammatik ist<declaration-specifier> <declarator>
Steve Cox
Sie haben Recht - ich habe nicht in den Standard geschaut, sondern nur die Regel aus meinem Kopf wiederholt. Tatsächlich kann in C ++ 11 die Rolle von <declaration-specifier>auch durch autoSchlüsselwörter gespielt werden , so dass es nicht immer ein Typ ist.
Ethouris
@Pravasi Meet: Wenn Sie den Teil meines Beitrags bearbeitet und die Namen im angegebenen Syntaxschema geändert haben, ändern Sie bitte auch die Namen in den folgenden 3 Zeilen entsprechend. Ansonsten gibt es noch alte Namen "Fronttyp" und "Spezifikation" und daher macht der Beitrag keinen Sinn.
Ethouris