Konstruktor mit Klammern aufrufen



  • Hallo zusammen,
    ich bin auf ein Problem gestoßen bei dem ich etwas irritiert bin, dass mir das noch nie begegnet ist.
    Und zwar habe ich eine einfache Klasse geschrieben:

    class Hallo
    {
        static int count;
        int bla;
        public:
        Hallo(){ count++; std::cout << "Init\n" << std::endl;}
        ~Hallo(){ count--;}
        int getBla();
    };
    

    um mit statischen Membern ein bisschen rumzuspielen. Aber dann habe ich ein Objekt dieser Klasse angelegt:

    Hallo foo();
    foo.getBla();
    

    und bekomme die Fehlermeldung:

    error: request for member ‘getBla’ in ‘foo’, which is of non-class type ‘Hallo()’

    Nehme ich den Konstruktor aber ohne die Klammern funktioniert es. Oder wenn ich das Objekt mit Hallo foo = Hallo() anlege auch.

    Gibt es da irgendwo eine Regel wann ich den selbst-definierten Default-Konstruktor mit und wann ohne Klammern aufrufen soll und wann mit...?

    Vielen Dank für Eure Antwort.

    /C



  • Hallo foo();
    

    Das deklariert eine Funktion mit Namen foo, die ein Objekt vom Typ Hallo zurückliefert.

    Hallo foo;
    

    Erzeugt ein Objekt foo.

    struct bar{...};
    Hallo foo(bar());
    

    Deklariert eine Funktion mit Namen foo, die als Parameter ein Objekt vom Typ bar erhält und ein Objekt vom Typ Hallo zurückliefert.

    Hallo foo{};
    

    Erzeugt ein Objekt foo (C++11).

    Hallo foo{bar()};
    

    Erzeugt ein Objekt foo, initialisiert mit einem default-konstruierten Objekt vom Typ bar.


  • Mod

    Hallo foo();
    

    Diese Deklaration deklariert eine Funktion (die keine Parameter hat und Hallo zurückgibt), keine Variable. Der Fehler nennt sich most vexing parse.

    Deklariert eine Funktion mit Namen foo, die als Parameter ein Objekt vom Typ bar erhält und ein Objekt vom Typ Hallo zurückliefert.

    Nein. Es deklariert eine Funktion die als Parameter einen Funktionszeiger auf eine Funktion ohne Parameter und bar als Rückgabetyp nimmt (Test hier).



  • Arcoth schrieb:

    Deklariert eine Funktion mit Namen foo, die als Parameter ein Objekt vom Typ bar erhält und ein Objekt vom Typ Hallo zurückliefert.

    Nein. Es deklariert eine Funktion die als Parameter einen Funktionszeiger auf eine Funktion ohne Parameter und bar als Rückgabetyp nimmt (Test hier).

    Jup, du hast Recht.
    Auf jeden Fall eine Funktion.


  • Mod

    Auf jeden Fall eine Funktion.

    😃



  • Hmmm, diese Funktionen: Hallo foo(); habe ich aber in main aufgerufen.
    gebe das zu bedenken.

    /C


  • Mod

    Hmmm, diese Funktionen: Hallo foo(); habe ich aber in main aufgerufen.
    gebe das zu bedenken.

    Du kannst nichts aufgerufen haben da die Funktion nirgends definiert ist. Du wirst einen Linker-Error bekommen falls du es versuchst.


  • Mod



  • Naja nicht wirklich aufgerufen, aber ich habe wie oben beschrieben damit versucht das Objekt vom Typ Hallo anzulegen. Innerhalb von main.

    /C



  • columbus schrieb:

    Naja nicht wirklich aufgerufen, aber ich habe wie oben beschrieben damit versucht das Objekt vom Typ Hallo anzulegen. Innerhalb von main.

    Nein. Der Compiler geht davon aus, dass du eine Funktion deklarierst. Der kommt gar nicht auf die Idee, dass du da ein Objekt anlegen wolltest.


  • Mod

    Der kommt gar nicht auf die Idee, dass du da ein Objekt anlegen wolltest.

    Wie kommt es dann dass Clang mich darauf hinweist dass ich, möglicherweise versehentlich, eine Funktion deklariere? Der Compiler sieht es, muss jedoch den Standard befolgen. Lokale Funktionsdeklarationen sind ein Unsinn.



  • Arcoth schrieb:

    Lokale Funktionsdeklarationen sind ein Unsinn.

    +1
    K.a. was man sich dabei gedacht hat.



  • Dabei muss man sich nichts denken, da es ohne weiteres aus der allgemeinen Syntax der Sprache folgt. Wenn man das nicht will, muss man eine Sonderregel einführen und es speziell verbieten. Das haben K&R natürlich nicht gemacht, weil es in C damit keine Probleme gibt. Wenn du das jetzt in C++ anders machen willst, dann wäre die Bedeutung von

    Objekt Hallo();
    

    davon abhängig, ob das in einer Funktion oder auf Namespace-Ebene steht ... *das* wäre ziemlich pervers.



  • Man entfernt einfach Deklarationen, Problem geloest. 🤡



  • Warum erlaubt man eigentlich nicht einfach auch lokale Funktionsdefinitionen? Dann würde so eine Deklaration ja wenigstens noch Sinn machen und konsistenter wäre es auch...



  • happystudent schrieb:

    Warum erlaubt man eigentlich nicht einfach auch lokale Funktionsdefinitionen? Dann würde so eine Deklaration ja wenigstens noch Sinn machen und konsistenter wäre es auch...

    Erlaubt man doch: Das heißt Lambda.

    Arcoth schrieb:

    Der kommt gar nicht auf die Idee, dass du da ein Objekt anlegen wolltest.

    Wie kommt es dann dass Clang mich darauf hinweist dass ich, möglicherweise versehentlich, eine Funktion deklariere? Der Compiler sieht es, muss jedoch den Standard befolgen.

    Du weißt, was ich sagen wollte. columbus dachte scheinbar immer noch, er würde da irgendetwas aufrufen, weil er gar nicht auf die Idee kam, dass der Compiler da etwas anderes reininterpretieren würde. Deswegen formulierte ich das so drastisch. Dein Beitrag trägt jetzt nur zur Verwirrung bei.



  • Nathan schrieb:

    Erlaubt man doch: Das heißt Lambda.

    Das ist aber nicht das gleiche, mit der selben Begründung könnte man ja sagen dass normale Funktionen auch nicht gebraucht werden, schließlich kann ich auch globale Lambdas benutzen:

    auto f1 = []{};
    void f2() {}
    
    int main() {
        f1();
        f2();
    }
    

    Allerdings würde bei einer normalen Funktion wohl niemand auf die Idee kommen ein Lambda zu verwenden. Die eigentliche Frage die sich mir stellt ist warum man Funktionen hier beschränkt und keine nested Funktionen erlaubt.

    Ich fände zum Beispiel

    void f1() {
        int f2() { return 1; }
        f2();
    }
    

    wesentlich "schöner" und expliziter zu lesen als

    void f1() {
        auto f2 = []()->int{ return 1; };
        f2();
    }
    


  • happystudent schrieb:

    wesentlich "schöner" und expliziter zu lesen als

    void f1() {
        auto f2 = []()->int{ return 1; };
        f2();
    }
    

    Innere Funktionen dürfen gerne auffallen, damit man sie nicht mit Blöcken verwechselt, zum Beispiel durch das Semikolon.

    Außerdem möchten lokale Funktionen meist auf Variablen der äußeren Funktion zugreifen, was man mit Lambda-Ausdrücken syntaktisch vernünftig gelöst hat.



  • happystudent schrieb:

    [...]

    Die Syntax von Lambdas ist halt anders und Lambdas sind auch keine "echten" Funktionen, sondern Klassenobjekte mit operator(). nested functions sind schwierig für den Compiler zu implementieren, u.a. auch wegen etwaigen Zugriff auf Variablen der umschließenden Funktion.
    Man hätte Lambdas auch die Syntax von normalen Funktionen geben können, aber das würde wohl zu sehr die Grammatik verändern und Ambiguitäten hervorrufen oder so, deswegen dieses "exotische" mit dem [].



  • TyRoXx schrieb:

    Außerdem möchten lokale Funktionen meist auf Variablen der äußeren Funktion zugreifen, was man mit Lambda-Ausdrücken syntaktisch vernünftig gelöst hat.

    Schon, aber man könnte ja problemlos beides anbieten, oder?

    Aber es stimmt wohl dass man eh in den allermeisten Fällen irgendwas capturen will. Ich find es halt nur komisch dass da diese Ausnahme existiert, weil alle anderen Sachen kann man beliebig nesten (structs, namespaces, etc.), nur Funktionen nicht.


Log in to reply