Unter den vielen Dingen, die mir Stack Overflow beigebracht hat, befindet sich die sogenannte "ärgerlichste Analyse", die klassisch mit einer Linie wie z
A a(B()); //declares a function
Während dies für die meisten, intuitiv die Deklaration eines Objekts zu sein scheint , a
vom Typ A
, eine temporäre Aufnahme B
Objekt als Konstruktorparameter, ist es tatsächlich eine Erklärung einer Funktion a
einer Rückkehr A
, einen Zeiger auf eine Funktion , wobei die zurückkehrt B
und sich keine Parameter . Ebenso die Linie
A a(); //declares a function
fällt ebenfalls unter dieselbe Kategorie, da anstelle eines Objekts eine Funktion deklariert wird. Im ersten Fall besteht die übliche Problemumgehung für dieses Problem darin, einen zusätzlichen Satz von Klammern / Klammern um das zu setzen B()
, da der Compiler ihn dann als Deklaration eines Objekts interpretiert
A a((B())); //declares an object
Im zweiten Fall führt dies jedoch zu einem Kompilierungsfehler
A a(()); //compile error
Meine Frage ist, warum? Ja, ich bin mir sehr wohl bewusst, dass die richtige 'Problemumgehung' darin besteht, sie zu ändern A a;
, aber ich bin gespannt, was das Extra ()
für den Compiler im ersten Beispiel bewirkt, das dann beim erneuten Anwenden nicht funktioniert das zweite Beispiel. Ist die A a((B()));
Problemumgehung eine bestimmte Ausnahme, die in den Standard geschrieben wurde?
(B())
ist nur ein C ++ - Ausdruck, nichts weiter. Es ist keine Ausnahme. Der einzige Unterschied besteht darin, dass es unmöglich ist, es als Typ zu analysieren, und das ist es auch nicht.A a();
ist nicht von der gleichen Kategorie. Für den Compiler gibt es keine andere Möglichkeit, ihn zu analysieren: Ein Initialisierer an dieser Stelle besteht niemals aus leeren Klammern, daher ist dies immer eine Funktionsdeklaration.A a();
ist kein Beispiel für die ärgerlichste Analyse . Es ist einfach eine Funktionsdeklaration, genau wie in C.A a;
" zu ändern "ist falsch. Dadurch erhalten Sie keine Initialisierung eines POD-Typs. Um die Initialisierung zu erhalten, schreiben SieA a{};
.Antworten:
Es gibt keine aufgeklärte Antwort, nur weil sie von der C ++ - Sprache nicht als gültige Syntax definiert wird ... So ist es auch, per Definition der Sprache.
Wenn Sie einen Ausdruck in sich haben, ist dieser gültig. Beispielsweise:
Noch einfacher ausgedrückt: weil
(x)
es sich um einen gültigen C ++ - Ausdruck handelt, während dies()
nicht der Fall ist.Um mehr darüber zu erfahren, wie Sprachen definiert werden und wie Compiler funktionieren, sollten Sie etwas über die formale Sprachtheorie oder insbesondere über kontextfreie Grammatiken (CFG) und verwandtes Material wie Finite-State-Maschinen lernen . Wenn Sie daran interessiert sind, obwohl die Wikipedia-Seiten nicht ausreichen, müssen Sie sich ein Buch besorgen.
quelle
(x)
es sich um einen gültigen C ++ - Ausdruck handelt, während dies()
nicht der Fall ist.Die endgültige Lösung für dieses Problem besteht darin, zur einheitlichen C + 11-Initialisierungssyntax zu wechseln, wenn Sie können.
http://www.stroustrup.com/C++11FAQ.html#uniform-init
quelle
C-Funktionsdeklaratoren
Zuallererst gibt es C. In C
A a()
ist Funktionsdeklaration. Hat zum Beispielputchar
die folgende Deklaration. Normalerweise werden solche Deklarationen in Header-Dateien gespeichert, aber nichts hindert Sie daran, sie manuell zu schreiben, wenn Sie wissen, wie die Funktionsdeklaration aussieht. Die Argumentnamen sind in Deklarationen optional, daher habe ich sie in diesem Beispiel weggelassen.Auf diese Weise können Sie den Code wie folgt schreiben.
Mit C können Sie auch Funktionen definieren, die Funktionen als Argumente verwenden, mit einer gut lesbaren Syntax, die wie ein Funktionsaufruf aussieht (gut, sie ist lesbar, solange Sie keinen Zeiger auf die Funktion zurückgeben).
Wie bereits erwähnt, erlaubt C das Weglassen von Argumentnamen in Header-Dateien, daher
output_result
würde dies in Header- Dateien so aussehen.Ein Argument im Konstruktor
Erkennst du das nicht? Nun, ich möchte Sie daran erinnern.
Ja, es ist genau die gleiche Funktionsdeklaration.
A
istint
,a
istoutput_result
undB
istint
.Sie können leicht einen Konflikt von C mit neuen Funktionen von C ++ feststellen. Um genau zu sein, Konstruktoren sind Klassenname und Klammer und alternative Deklarationssyntax mit
()
anstelle von=
. C ++ versucht von Natur aus, mit C-Code kompatibel zu sein, und muss sich daher mit diesem Fall befassen - auch wenn es praktisch niemanden interessiert. Daher haben alte C-Funktionen Vorrang vor neuen C ++ - Funktionen. Die Grammatik der Deklarationen versucht, den Namen als Funktion abzugleichen, bevor sie zur neuen Syntax zurückkehrt,()
wenn dies fehlschlägt.Wenn eine dieser Funktionen nicht vorhanden wäre oder eine andere Syntax hätte (wie
{}
in C ++ 11), wäre dieses Problem bei der Syntax mit einem Argument niemals aufgetreten.Jetzt können Sie fragen, warum
A a((B()))
funktioniert. Nun, lassen Sie unsoutput_result
mit nutzlosen Klammern erklären .Es wird nicht funktionieren. Die Grammatik erfordert, dass die Variable nicht in Klammern steht.
C ++ erwartet hier jedoch einen Standardausdruck. In C ++ können Sie den folgenden Code schreiben.
Und der folgende Code.
C ++ erwartet, dass der Ausdruck in Klammern ... nun ja ... Ausdruck ist, im Gegensatz zu dem Typ, den C erwartet. Klammern bedeuten hier nichts. Durch Einfügen nutzloser Klammern wird die C-Funktionsdeklaration jedoch nicht abgeglichen, und die neue Syntax kann ordnungsgemäß abgeglichen werden (was lediglich einen Ausdruck erwartet, z. B.
2 + 2
).Weitere Argumente im Konstruktor
Sicherlich ist ein Argument nett, aber was ist mit zwei? Es ist nicht so, dass Konstruktoren nur ein Argument haben könnten. Eine der integrierten Klassen, die zwei Argumente akzeptiert, ist
std::string
Das ist alles gut und schön (technisch gesehen wäre es am ärgerlichsten, wenn es so geschrieben würde
std::string wat(int(), char())
, aber seien wir ehrlich - wer würde das schreiben? Aber nehmen wir an, dieser Code hat ein ärgerliches Problem. Sie würden annehmen, dass Sie setzen müssen alles in Klammern.Nicht ganz so.
Ich bin mir nicht sicher , warum g ++ versucht zu konvertieren
char
zuconst char *
. In beiden Fällen wurde der Konstruktor mit nur einem Wert vom Typ aufgerufenchar
. Es gibt keine Überladung mit einem Argument vom Typchar
, daher ist der Compiler verwirrt. Sie fragen sich vielleicht, warum das Argument vom Typ char ist.Ja,
,
hier ist ein Kommaoperator. Der Kommaoperator akzeptiert zwei Argumente und gibt das Argument auf der rechten Seite an. Es ist nicht wirklich nützlich, aber es ist etwas, das für meine Erklärung bekannt sein muss.Stattdessen wird der folgende Code benötigt, um die ärgerlichste Analyse zu lösen.
Die Argumente stehen in Klammern, nicht der gesamte Ausdruck. Tatsächlich muss nur einer der Ausdrücke in Klammern stehen, da es ausreicht, leicht von der C-Grammatik abzuweichen, um die C ++ - Funktion zu verwenden. Die Dinge bringen uns an den Punkt der Nullargumente.
Keine Argumente im Konstruktor
Möglicherweise haben Sie die
eighty_four
Funktion in meiner Erklärung bemerkt .Ja, dies wird auch von der ärgerlichsten Analyse beeinflusst. Es ist eine gültige Definition, die Sie höchstwahrscheinlich gesehen haben, wenn Sie Header-Dateien erstellt haben (und sollten). Das Hinzufügen von Klammern behebt das Problem nicht.
Warum ist das so? Nun,
()
ist kein Ausdruck. In C ++ müssen Sie einen Ausdruck in Klammern setzen. Sie können nichtauto value = ()
in C ++ schreiben , da()
dies nichts bedeutet (und selbst wenn dies der Fall wäre, wie ein leeres Tupel (siehe Python), wäre es ein Argument, nicht Null). Praktisch bedeutet dies, dass Sie die Kurzschrift-Syntax nicht verwenden können, ohne die Syntax von C ++ 11 zu verwenden{}
, da keine Ausdrücke in Klammern gesetzt werden müssen und die C-Grammatik für Funktionsdeklarationen immer gilt.quelle
Sie könnten stattdessen
verwenden
quelle
int a = int();
Initialisierta
mit 0,int a;
lässta
nicht initialisiert. Eine korrekte Problemumgehung besteht darin, sieA a = {};
für Aggregate zu verwenden,A a;
wenn die Standardinitialisierung das tut, was Sie möchten, undA a = A();
in allen anderen Fällen - oder einfachA a = A();
konsistent zu verwenden. In C ++ 11 verwenden Sie einfachA a {};
Die innersten Parens in Ihrem Beispiel wären ein Ausdruck, und in C ++ definiert die Grammatik ein
expression
als das eineassignment-expression
oder andere,expression
gefolgt von einem Komma und einem anderenassignment-expression
(Anhang A.4 - Grammatikzusammenfassung / Ausdrücke).Die Grammatik definiert einen
assignment-expression
als einen von mehreren anderen Ausdruckstypen, von denen keiner nichts (oder nur Leerzeichen) sein kann.Der Grund, den Sie nicht haben können,
A a(())
ist einfach, weil die Grammatik es nicht zulässt. Ich kann jedoch nicht antworten, warum die Leute, die C ++ erstellt haben, diese spezielle Verwendung von leeren Parens nicht als Sonderfall zugelassen haben - ich würde vermuten, dass sie einen solchen Sonderfall lieber nicht einführen würden, wenn es einen gäbe eine vernünftige Alternative.quelle