Mein Versuch der Wertinitialisierung wird als Funktionsdeklaration interpretiert, und warum nicht A a(()); löse es?

Lesezeit: 9 Minuten

Mein Versuch der Wertinitialisierung wird als Funktionsdeklaration interpretiert und warum
GRB

Unter den vielen Dingen, die mir Stack Overflow beigebracht hat, ist das, was als “ärgerlichste Analyse” bekannt ist, was klassischerweise mit einer Zeile wie demonstriert wird

A a(B()); //declares a function

Während dies für die meisten intuitiv die Deklaration eines Objekts zu sein scheint a des Typs Anehmen eine vorübergehende B object als Konstruktorparameter, es ist eigentlich eine Deklaration einer Funktion a Rückkehr ein Awobei ein Zeiger auf eine Funktion genommen wird, die zurückkehrt B und selbst nimmt keine Parameter an. Ebenso die Linie

A a(); //declares a function

fällt ebenfalls unter dieselbe Kategorie, da es anstelle eines Objekts eine Funktion deklariert. Nun, im ersten Fall besteht die übliche Problemumgehung für dieses Problem darin, einen zusätzlichen Satz Klammern/Klammern um die hinzuzufügen B()da der Compiler es dann als Deklaration eines Objekts interpretiert

A a((B())); //declares an object

Im zweiten Fall führt dies jedoch zu einem Kompilierfehler

A a(()); //compile error

Meine Frage ist, warum? Ja, ich bin mir sehr wohl bewusst, dass die richtige „Problemumgehung“ darin besteht, es zu ändern A a;aber ich bin neugierig zu wissen, was es ist, dass das Extra () tut für den Compiler im ersten Beispiel, was dann nicht funktioniert, wenn es im zweiten Beispiel erneut angewendet wird. Ist der A a((B())); Problemumgehung eine bestimmte Ausnahme, die in den Standard geschrieben wurde?

  • (B()) ist nur ein C++-Ausdruck, mehr nicht. Es ist nicht irgendeine Art von Ausnahme. Der einzige Unterschied, den es macht, ist, dass es unmöglich als Typ geparst werden kann, und das ist es auch nicht.

    – Pavel Minaev

    15. September 2009 um 0:32 Uhr

  • Es sollte auch beachtet werden, dass der zweite Fall, A a(); ist nicht derselben Kategorie. Für die Compilergibt es nie einen anderen Weg, es zu analysieren: Ein Initialisierer an dieser Stelle besteht nie aus leeren Klammern, also ist dies immer eine Funktionsdeklaration.

    – Johannes Schaub – litb

    15. September 2009 um 2:08 Uhr

  • Der hervorragende Punkt von litb ist subtil, aber wichtig und sollte betont werden – der Grund für die Mehrdeutigkeit in dieser Deklaration ‘A a (B())’ liegt in der Analyse von ‘B()’ -> es kann sowohl ein Ausdruck & eine Deklaration & der Compiler muss decl über expr ‘picken’ – wenn also B() ein decl ist, dann kann ‘a’ nur ein func decl sein (kein variables decl). Wenn ‘()’ ein Initialisierer sein dürfte, wäre ‘A a()’ mehrdeutig – aber nicht expr vs decl, sondern var decl vs func decl – es gibt keine Regel, ein decl einem anderen vorzuziehen – und so ‘() ‘ ist hier als Initialisierer einfach nicht erlaubt – und die Mehrdeutigkeit steigt nicht.

    – Faisal Vali

    15. September 2009 um 4:29 Uhr

  • A a(); ist nicht ein Beispiel für die ärgerlichste Analyse. Es ist einfach eine Funktionsdeklaration, genau wie in C.

    – Peter Becker

    27. März 2013 um 11:32 Uhr


  • “Der richtige ‘Workaround’ ist, es zu ändern A a;” ist falsch. Dadurch erhalten Sie keine Initialisierung eines POD-Typs. Um die Initialisierung zu erhalten, schreiben Sie A a{};.

    – Prost und hth. – Alf

    1. September 2017 um 17:33 Uhr

1647293413 634 Mein Versuch der Wertinitialisierung wird als Funktionsdeklaration interpretiert und warum
Brian R. Bondy

Es gibt keine erleuchtete Antwort, es liegt nur daran, dass es von der C ++ – Sprache nicht als gültige Syntax definiert ist … So ist es per Definition der Sprache.

Wenn Sie einen Ausdruck darin haben, dann ist er gültig. Zum Beispiel:

 ((0));//compiles

Noch einfacher ausgedrückt: weil (x) ist ein gültiger C++-Ausdruck, während () ist nicht.

Um mehr darüber zu erfahren, wie Sprachen definiert werden und wie Compiler funktionieren, sollten Sie sich darüber informieren Formale Sprachtheorie oder genauer gesagt Kontextfreie Grammatiken (CFG) und verwandtes Material wie endliche Zustandsautomaten. Wenn Sie daran interessiert sind, obwohl die Wikipedia-Seiten nicht ausreichen, müssen Sie sich ein Buch besorgen.

  • Noch einfacher ausgedrückt: weil (x) ist ein gültiger C++-Ausdruck, während () ist nicht.

    – Pavel Minaev

    15. September 2009 um 0:31 Uhr

  • Ich habe diese Antwort akzeptiert, außerdem hat mir Pavels Kommentar zu meiner ursprünglichen Frage sehr geholfen

    – GRB

    15. September 2009 um 0:40 Uhr

Mein Versuch der Wertinitialisierung wird als Funktionsdeklaration interpretiert und warum
David

Die endgültige Lösung für dieses Problem besteht darin, wenn möglich zur einheitlichen C+11-Initialisierungssyntax zu wechseln.

A a{};

http://www.stroustrup.com/C++11FAQ.html#uniform-init

Mein Versuch der Wertinitialisierung wird als Funktionsdeklaration interpretiert und warum
Konrad Borowski

C-Funktionsdeklaratoren

Zunächst einmal gibt es C. In C, A a() ist eine Funktionsdeklaration. Zum Beispiel, putchar hat 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.

int putchar(int);

Dadurch können Sie den Code wie folgt schreiben.

int puts(const char *);
int main() {
    puts("Hello, world!");
}

C erlaubt Ihnen auch, Funktionen zu definieren, die Funktionen als Argumente annehmen, mit einer gut lesbaren Syntax, die wie ein Funktionsaufruf aussieht (na ja, es ist lesbar, solange Sie keinen Zeiger auf die Funktion zurückgeben).

#include <stdio.h>

int eighty_four() {
    return 84;
}

int output_result(int callback()) {
    printf("Returned: %d\n", callback());
    return 0;
}

int main() {
    return output_result(eighty_four);
}

Wie ich bereits erwähnt habe, erlaubt C das Weglassen von Argumentnamen in Header-Dateien, daher die output_result würde in der Header-Datei so aussehen.

int output_result(int());

Ein Argument im Konstruktor

Erkennst du den nicht? Nun, lassen Sie mich Sie daran erinnern.

A a(B());

Ja, es ist genau die gleiche Funktionsdeklaration. A ist int, a ist output_resultund B ist int.

Sie können leicht einen Konflikt von C mit neuen Features von C++ erkennen. Um genau zu sein, Konstruktoren sind Klassenname und Klammern und alternative Deklarationssyntax mit () anstatt =. C++ versucht per Design, mit C-Code kompatibel zu sein, und muss sich daher mit diesem Fall auseinandersetzen – auch wenn es praktisch niemanden interessiert. Daher haben alte C-Features Vorrang vor neuen C++-Features. Die Grammatik der Deklarationen versucht, den Namen als Funktion abzugleichen, bevor sie mit zur neuen Syntax zurückkehrt () wenn es fehlschlägt.

Wenn eine dieser Funktionen nicht vorhanden wäre oder eine andere Syntax hätte (z {} in C++11), wäre dieses Problem bei einer Syntax mit einem Argument nie aufgetreten.

Jetzt fragen Sie vielleicht warum A a((B())) funktioniert. Nun, lassen Sie uns erklären output_result mit nutzlosen Klammern.

int output_result((int()));

Es wird nicht funktionieren. Die Grammatik erfordert, dass die Variable nicht in Klammern steht.

<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token

C++ erwartet hier jedoch einen Standardausdruck. In C++ können Sie den folgenden Code schreiben.

int value = int();

Und der folgende Code.

int value = ((((int()))));

C++ erwartet, dass der Ausdruck innerhalb von Klammern … na ja … Ausdruck ist, im Gegensatz zu dem Typ, den C erwartet. Klammern bedeuten hier nichts. Durch das Einfügen nutzloser Klammern wird die C-Funktionsdeklaration jedoch nicht abgeglichen, und die neue Syntax kann richtig abgeglichen werden (was einfach einen Ausdruck wie z 2 + 2).

Weitere Argumente im Konstruktor

Sicherlich ist ein Argument schön, aber was ist mit zwei? Es ist nicht so, dass Konstruktoren möglicherweise nur ein Argument haben. Eine der eingebauten Klassen, die zwei Argumente akzeptiert, ist std::string

std::string hundred_dots(100, '.');

Das ist alles schön und gut (technisch gesehen hätte es die ärgerlichste Analyse, wenn es so geschrieben würde std::string wat(int(), char()), aber mal ehrlich – wer würde das schreiben? Aber nehmen wir an, dieser Code hat ein ärgerliches Problem. Sie würden davon ausgehen, dass Sie alles in Klammern setzen müssen.

std::string hundred_dots((100, '.'));

Nicht ganz so.

<stdin>:2:36: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
In file included from /usr/include/c++/4.8/string:53:0,
                 from <stdin>:1:
/usr/include/c++/4.8/bits/basic_string.tcc:212:5: error:   initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
     basic_string<_CharT, _Traits, _Alloc>::
     ^

Ich bin mir nicht sicher, warum g++ versucht zu konvertieren char zu const char *. In beiden Fällen wurde der Konstruktor mit nur einem Wert vom Typ aufgerufen char. Es gibt keine Überladung, die ein Argument vom Typ hat char, daher ist der Compiler verwirrt. Sie fragen sich vielleicht – warum das Argument vom Typ char ist?

(100, '.')

Jawohl, , Hier ist ein Komma-Operator. Der Kommaoperator nimmt zwei Argumente und gibt das Argument auf der rechten Seite zurück. Es ist nicht wirklich nützlich, aber es ist etwas, das für meine Erklärung bekannt sein sollte.

Um die ärgerlichste Analyse zu lösen, wird stattdessen der folgende Code benötigt.

std::string hundred_dots((100), ('.'));

Die Argumente stehen in Klammern, nicht der gesamte Ausdruck. Tatsächlich muss nur einer der Ausdrücke in Klammern stehen, da es ausreicht, geringfügig von der C-Grammatik abzuweichen, um die C++-Funktion zu verwenden. Things bringt uns an den Punkt der Null-Argumente.

Null Argumente im Konstruktor

Vielleicht ist Ihnen das schon aufgefallen eighty_four Funktion in meiner Erklärung.

int eighty_four();

Ja, das ist auch von der ärgerlichsten Analyse betroffen. Es ist eine gültige Definition, die Sie höchstwahrscheinlich gesehen haben, wenn Sie Header-Dateien erstellt haben (und das sollten Sie auch). Das Hinzufügen von Klammern behebt es nicht.

int eighty_four(());

Warum ist das so? Brunnen, () ist kein Ausdruck. In C++ müssen Sie einen Ausdruck in Klammern setzen. Du kannst nicht schreiben auto value = () in C++, weil () bedeutet nichts (und selbst wenn, wie ein leeres Tupel (siehe Python), wäre es ein Argument, nicht null). Praktisch bedeutet dies, dass Sie keine Kurzschriftsyntax verwenden können, ohne C++ 11 zu verwenden {} Syntax, da keine Ausdrücke in Klammern gesetzt werden müssen und die C-Grammatik für Funktionsdeklarationen immer gilt.

Sie könnten stattdessen

A a(());

verwenden

A a=A();

Die innersten Klammern in Ihrem Beispiel wären ein Ausdruck, und in C++ definiert die Grammatik an expression ein … sein assignment-expression oder ein anderes expression gefolgt von einem Komma und einem weiteren assignment-expression (Anhang A.4 – Grammatikzusammenfassung/Ausdrücke).

Die Grammatik definiert weiter an assignment-expression als eine von mehreren anderen Ausdrucksarten, von denen keine nichts (oder nur Leerzeichen) sein kann.

Also der Grund, den Sie nicht haben können A a(()) liegt einfach daran, dass die Grammatik es nicht zulässt. Ich kann jedoch nicht beantworten, warum die Leute, die C++ erstellt haben, diese spezielle Verwendung leerer Klammern nicht als eine Art Sonderfall zugelassen haben – ich würde vermuten, dass sie einen solchen Sonderfall lieber nicht einsetzen würden, wenn es einen gäbe eine sinnvolle Alternative.

1003190cookie-checkMein Versuch der Wertinitialisierung wird als Funktionsdeklaration interpretiert, und warum nicht A a(()); löse es?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy