Was heißt: warning: type qualifiers ignored on function return type ?



  • dot schrieb:

    Das Objekt, das hier zurückgegeben wird, ist eine Kopie und keine Referenz. Es kann sich niemals um das selbe Objekt handeln, das irgendeine Klasse "intern verwendet" und es wäre in jedem Fall sicher, daraus zu moven (außer natürlich die Implementierung der Klasse macht irgendeinen argen Blödsinn)...

    Wieso? Wenn ich eine member-Variable der Klasse zurückliefere?
    Normalerweise würde dann, falls vorhanden der Move-Constructor aufgerufen, wodurch meine Member-Variable 'kaputt' gehen könnte...



  • XSpille schrieb:

    dot schrieb:

    Das Objekt, das hier zurückgegeben wird, ist eine Kopie und keine Referenz. Es kann sich niemals um das selbe Objekt handeln, das irgendeine Klasse "intern verwendet" und es wäre in jedem Fall sicher, daraus zu moven (außer natürlich die Implementierung der Klasse macht irgendeinen argen Blödsinn)...

    Wieso? Wenn ich eine member-Variable der Klasse zurückliefere?
    Normalerweise würde dann, falls vorhanden der Move-Constructor aufgerufen, wodurch meine Member-Variable 'kaputt' gehen könnte...

    Ähm, nein!?

    X blub()
    {
      return my_x;
    }
    

    Wo wird hier ein Move Konstruktor aufgerufen?



  • XSpille schrieb:

    Wieso? Wenn ich eine member-Variable der Klasse zurückliefere?

    Tust du ja auf keinen Fall, höchstens eine Referenz auf die Member-Variable...



  • dot schrieb:

    Wo wird hier ein Move Konstruktor aufgerufen?

    Irrtum meinerseits 🙂
    Habs gerade auch getestet.

    test.cpp:32:10: error: use of deleted function 'constexpr Foo::Foo(const Foo&)'
    test.cpp:4:8: note: 'constexpr Foo::Foo(const Foo&)' is implicitly declared as deleted because 'Foo' declares a move constructor or move assignment operator

    pumuckl schrieb:

    XSpille schrieb:

    Wieso? Wenn ich eine member-Variable der Klasse zurückliefere?

    Tust du ja auf keinen Fall, höchstens eine Referenz auf die Member-Variable...

    Stimmt... Gute Begründung...

    Naja... Dann fällt mir doch kein Sinn für das const ein 🤡



  • XSpille schrieb:

    dot schrieb:

    Wo wird hier ein Move Konstruktor aufgerufen?

    Irrtum meinerseits 🙂

    Wenn my_x eine lokale Variable wäre, würde, falls vorhanden, der Move Konstruktor aufgerufen. Aber in dem Fall ist das auch safe... 😉



  • dot schrieb:

    Ist State ein typedef auf einen primitiven Typ, oder eine Klasse?

    Edit: Ok, die Frage ist eigentlich nur im Kontext mit Operator Overloading relevant, im Falle einer normalen Funktion ist ein top level const am Returntype in der Tat sinnlos...

    Das ist nicht nur bei Operator Overlöding interessant, sondern auch bei sowas

    getLocalState() = Blubb; // geht mit "UDT getLocalState()", geht nicht mit "const UDT getLocalState()"
    

    Finde ich aber grässlich.
    In C++11 kann man das wesentlich schöner verhindern indem man den Zuweisungsoperator "&" macht (also UDT& operator = (ARG) & {... schreibt, und damit verbietet dass this an eine rvalue gebunden wird).



  • hustbaer schrieb:

    dot schrieb:

    Ist State ein typedef auf einen primitiven Typ, oder eine Klasse?

    Edit: Ok, die Frage ist eigentlich nur im Kontext mit Operator Overloading relevant, im Falle einer normalen Funktion ist ein top level const am Returntype in der Tat sinnlos...

    Das ist nicht nur bei Operator Overlöding interessant, sondern auch bei sowas

    getLocalState() = Blubb; // geht mit "UDT getLocalState()", geht nicht mit "const UDT getLocalState()"
    

    Tatsächlich, ich hab mir irgendwie immer eingebildet, dass ein solcher Function Call ein rvalue wäre...

    hustbaer schrieb:

    In C++11 kann man das wesentlich schöner verhindern indem man den Zuweisungsoperator "&" macht (also UDT& operator = (ARG) & {... schreibt, und damit verbietet dass this an eine rvalue gebunden wird).

    Nanu, wie nennt sich diese Syntax denn, das hab ich noch nie gesehn und im Standard und bei google kann ich irgendwie auch nix dazu finden!?



  • Das nennt sich Ref-Qualifier, wird aber bei den Compilern als "Rvalue References for *this" gelistet. http://stackoverflow.com/questions/8610571/what-is-rvalue-reference-for-this



  • Sehr interessantes Feature das mir bisher unbekannt war, danke für den Hinweis!



  • dot schrieb:

    hustbaer schrieb:

    dot schrieb:

    Ist State ein typedef auf einen primitiven Typ, oder eine Klasse?

    Edit: Ok, die Frage ist eigentlich nur im Kontext mit Operator Overloading relevant, im Falle einer normalen Funktion ist ein top level const am Returntype in der Tat sinnlos...

    Das ist nicht nur bei Operator Overlöding interessant, sondern auch bei sowas

    getLocalState() = Blubb; // geht mit "UDT getLocalState()", geht nicht mit "const UDT getLocalState()"
    

    Tatsächlich, ich hab mir irgendwie immer eingebildet, dass ein solcher Function Call ein rvalue wäre...

    Ist es ja auch (das Ergebnis des Funktionsaufrufs, der Funktionsaufruf selbär ist gar keine Value sondern ein Funktionsaufruf :D).
    Nur darf man Memberfunktionen per Default auf RValues aufrufen. Sonst würde Funktion().AndereFunktion() nicht gehen. Geht aber. So lange man AndereFunktion nicht Ref-qualifiziert.

    Und da der operator = bei UDTs per Default ne Memberfunktion ist, kann man auch Funktion().operator = (blubb) schreiben -- und Funktion() = blubb ist ja genau das selbe, bloss anders geschrieben.

    Ergo geht es.

    Sobald man dann operator = ref-qualifizierterweise selbst definiert, geht es nimmer.

    BTW: Angenommen man möchte den operator = ref-qualifiziert haben, aber sonst nix ändern (also genau die Implementierung die der Compiler von sich aus machen würde)...
    Für den Fall würde sich ja

    UDT& operator = (UDT const&) & = default;
    

    als Syntax anbieten.
    Weiss jmd. ob das erlaubt ist? Würde im Falle des Falles nämlich viel sinnlose Tipperei ersparen und hätte dank Wartungsfreiheit auch ein geringeres Fehlerpotential (Membervariable dazumachen, nicht an operator = denken -> Problem).



  • hustbaer schrieb:

    Ist es ja auch (das Ergebnis des Funktionsaufrufs, der Funktionsaufruf selbär ist gar keine Value sondern ein Funktionsaufruf :D).

    Naja, ich meinte natürlich den Wert der Function Call Expression... 😉

    hustbaer schrieb:

    Nur darf man Memberfunktionen per Default auf RValues aufrufen. Sonst würde Funktion().AndereFunktion() nicht gehen. Geht aber. So lange man AndereFunktion nicht Ref-qualifiziert.

    Und da der operator = bei UDTs per Default ne Memberfunktion ist, kann man auch Funktion().operator = (blubb) schreiben -- und Funktion() = blubb ist ja genau das selbe, bloss anders geschrieben.

    Ergo geht es.

    Sobald man dann operator = ref-qualifizierterweise selbst definiert, geht es nimmer.

    Ach, stupid me, natürlich. Aber wenn ich das richtig sehe, braucht man im klassischen Paradebeispiel immer noch ein top level const am Return Type:

    vector3d operator +(const vector3d& a, const vector3d& b);
    
    (x + y) = stuff;  // kompiliert fröhlich vor sich hin
    

    hustbaer schrieb:

    BTW: Angenommen man möchte den operator = ref-qualifiziert haben, aber sonst nix ändern (also genau die Implementierung die der Compiler von sich aus machen würde)...
    Für den Fall würde sich ja

    UDT& operator = (UDT const&) & = default;
    

    als Syntax anbieten.
    Weiss jmd. ob das erlaubt ist? Würde im Falle des Falles nämlich viel sinnlose Tipperei ersparen und hätte dank Wartungsfreiheit auch ein geringeres Fehlerpotential (Membervariable dazumachen, nicht an operator = denken -> Problem).

    Ich denke schon:

    ISO/IEC 14882:2011 §8.4.2/1 schrieb:

    [...] have the same declared function type (except for possibly differing ref-qualifiers and except that in the case of a copy constructor or copy assignment operator, the parameter type may be "reference to non-const T", where T is the name of the member function's class) as if it had been implicitly declared, [...]



  • Wie waere es mit:

    T& operator = (T const&) && = delete;
    


  • Ah natürlich, der ref-qualifier gehört ja zur Signatur, nice!

    Jetzt wär nur noch interessant, ob man mehr als eine Variante von operator = als defaulted definieren kann, wenn der Unterschied nur den ref-qualifier betrifft!? Ich vermute mal ja!?



  • dot schrieb:

    Aber wenn ich das richtig sehe, braucht man im klassischen Paradebeispiel immer noch ein top level const am Return Type:

    vector3d operator +(const vector3d& a, const vector3d& b);
    
    (x + y) = stuff;  // kompiliert fröhlich vor sich hin
    

    Wenn vector3d einen "&" qualifizierten operator = hat darf das eigentlich nicht sein. (Ohne geht es natürlich, ging immer, war auch immer schon etwas komisch. Aber naja, real hab ich in > 12 Jahren damit noch nie ein Problem gehabt.)

    Gerade ausprobiert mit clang online:

    struct foo {
        foo() {}
    };
    
    foo operator + (const foo&, const foo&) { return foo(); }
    
    struct bar {
        bar() {}
        bar& operator = (bar const&) & = default;
    };
    
    bar operator + (const bar&, const bar&) { return bar(); }
    
    int main()
    {
       foo a, b, c;
       bar x, y, z;
       (a + b) = c; // OK
       (x + y) = z; // error: no viable overloaded '='
                    //     (x + y) = z;
                    //     ~~~~~~~ ^ ~
    }
    

    ----

    Kellerautomat schrieb:

    Wie waere es mit:

    T& operator = (T const&) && = delete;
    

    Das sollte unnötig sein. Wenn man nen T& operator = (T&) definiert bekommt man ja auch keinen implizit definierten T& operator = (const T&) dazu.
    Genau so bekommt man keinen implizit definierten T& operator = (const T&) dazu wenn man T& operator = (const T&) & selbst definiert hat.



  • sven_ schrieb:

    ich stehe gerade etwas geistig auf dem Schlauch, ich habe folgende Fehlermeldung:

    warning: type qualifiers ignored on function return type
    

    Die entsteht in der Zeile:

    const State getLocalState() const;
    

    Ich sehe den Wald vor lauter Bäumen gerade nicht. Kann mich jemand draufstoßen?

    Das ist keine Fehlermeldung. Das ist eine Warnung. Und diese Warnung finde ich relativ aussagekräftig. Ob Du da jetzt ein const hinschreibst oder peng, ist vollkommen Latte. Das ist die Art von Warnung, die man bei bei skalaren Typen erwarten würde; denn ein PRvalue (pure rvalue) eines skalaren Typs ist wirklich nur ein Wert und bezieht sich gar nicht auf ein Objekt -- genauso wie 5 nur ein Wert ist und sich nicht auf ein Objekt bezieht. Änderbar können jedoch nur Objekte sein, weswegen hier das const überflüssig ist.

    Special thanks to camper, der mich in der Hinsicht mal auf den richtigen Trichter gebracht hat.

    hth



  • hustbaer schrieb:

    Kellerautomat schrieb:

    Wie waere es mit:

    T& operator = (T const&) && = delete;
    

    Das sollte unnötig sein. Wenn man nen T& operator = (T&) definiert bekommt man ja auch keinen implizit definierten T& operator = (const T&) dazu.
    Genau so bekommt man keinen implizit definierten T& operator = (const T&) dazu wenn man T& operator = (const T&) & selbst definiert hat.

    Dazu sei erwähnt, dass man beim Überladen das Vorhandensein und das Fehlen von Ref-Qualifizierern nicht mischen kann. Entweder sind Ref-Qualifizierer bei allen Overloads einer Overloadmenge vorhanden, oder sie fehlen überall. Mischmasch ist nicht erlaubt. Die Überladungsauflösung mit Ref-Qualifizierern läuft dann so, wie man es bei einem zusätzlichen Referenz-Parameter erwarten würde: "&" bindet nur non-const lvalues, "const&" frisst alles (lvalues und rvalues) kann aber *this natürlich nicht verändern, "&&" frisst nur rvalues, etc...



  • Uff.
    Auch interessant.
    Wusste ich nicht.


Anmelden zum Antworten