attempting to reference a deleted function



  • Hallo.

    Verstehe nicht wo hier der Fehler sein soll.

    Ich habe eine Klasse in der ich die move Funktionalität als deleted gekennzeichnet habe.

    class CoordinatePointList :
           public SpecialVector<CoordinatePoint>
       {
    
       public:
    
           explicit CoordinatePointList(int first=0, int last=0);
           virtual ~CoordinatePointList() = default;
    
           CoordinatePointList(const CoordinatePointList& other) = default;
           CoordinatePointList(CoordinatePointList&& other) noexcept = delete;
           CoordinatePointList& operator=(const CoordinatePointList& other) = default;
           CoordinatePointList& operator=(CoordinatePointList&& other) noexcept = delete;
    }
    

    In einer anderen Klasse und erzeuge ich mir ein Objekt dieser Klasse und gebe es als Kopie zurück.

    template <>
    CoordinatePointList UnitCutting<ContourInputDataPoint>::CalcCircularCuttingPath(const int p1, const int p2, const int r, const Lane lane,
                                                                                    const CGeometric::PosCoC posCoC) const
    {
        return CalcCircularCuttingPath(_inputData._pointlist[p1],
                                       _inputData._pointlist[p2],
                                       _inputData._radiuslist[r], lane, posCoC);
    }
    
    template <typename Tdata>
    CoordinatePointList UnitCutting<Tdata>::CalcCircularInsidePath(CoordinatePoint p1,
                                                              CoordinatePoint p2,
                                                              CoordinatePoint coc,
                                                              double r, double rTool) const
    {
       //...
        CoordinatePointList list(1,2);
       //...
    
        return list;
    }
    
    

    Doch bei "return list;" erhalte ich die Meldung:

    No viable constructor
    Candidates considered:
    calculation:CoordinatePointList CoordinatePointList::CoordinatePointList(const CoordinatePointList& other)
    calculation:CoordinatePointList CoordinatePointList::CoordinatePointList(CoordinatePointList&& other) noexcept
    

    und schliesslich dort wo ich den Aufruf mache

    auto list = CalcCircularCuttingPath(2,1,1,InsideLane,CGeometric::Top);
    

    Fehlermeldung:

    'calculation::CoordinatePointList &calculation::CoordinatePointList::operator =(calculation::CoordinatePointList &&) noexcept': attempting to reference a deleted function

    Wieso. Es wird doch nirgends move verwendet? Oder doch?



  • Doch sicher, das was rechts vom = steht, wird ja gemovet, weil du da ja ein temporäres Objekt stehen hast.

    Einfacheres Beispiel:

    struct Foo {
        Foo(int) {};
    
        Foo(const Foo& other) = default;
        Foo& operator=(const Foo& other) = default;
    
        Foo(Foo&& other) noexcept = delete;
        Foo& operator=(Foo&& other) noexcept = delete;
    };
    
    int main() {
        auto f = Foo(42);
    }
    

    Beachte, dass Foo f(42) funktioniert. Es ist übrigens egal, ob dann nach dem Kompilieren gar kein move aufgerufen mehr wird und die Variable gleich an der richtigen Stelle erzeugt wird - von der Syntax her ist es ein move und das hast du eben verboten.

    Mit clang kommt übrigens als Fehler:

    ~/test/delete.C:12:10: error: call to deleted constructor of 'Foo'
        auto f = Foo(42);
             ^   ~~~~~~~
    ~/test/delete.C:7:5: note: 'Foo' has been explicitly marked deleted here
        Foo(Foo&& other) noexcept = delete;
        ^
    


  • Ok.
    Verstehe ich noch nicht ganz.

    Obwohl ich keine move semantik anwende wird trotzdem gemovt. Der Code war ja auch bereits vor c++ 11 gültig.
    Also den Aufruf meine ich. Vor c++ 11 wurde dann was gemacht?



  • Vor c++11 gab es kein move semantic, da wurde dann vermutlich kopiert.
    Die Frage ist wiso möchtest du die move semantic hier verbieten? Ich sehe in dem Beispiel kein grund dazu. Ganz besonderes, wenn du für den copy constructor/assignment operator eh die vom compiler erzeugten versionen verwendest.



  • @firefly sagte in attempting to reference a deleted function:

    Die Frage ist wiso möchtest du die move semantic hier verbieten? I

    Will die eigentlich gar nicht verbieten. Ich gehe nur gerade einen alten Code durch und füge dabei für jede Klasse die Kopierkonstruktoren und Zuweisungsoperatoren ein. Und da ich (noch) nicht weiß ob sich das Verhalten meines Programmes ändert wenn ich die movesemantik anwende habe ich diese erst mal als deleted gekennzeichnet.

    Wenn beim kompilieren kein Fehler kommt sollte es ja passen.

    Nun erhalte ich aber einen Fehler und mir war dann nicht klar wieso nun an dieser Stelle einen Fehler kommt obwohl ich nirgends (bewusst) move semantik angewendet habe.



  • @booster sagte in attempting to reference a deleted function:

    füge dabei für jede Klasse die Kopierkonstruktoren und Zuweisungsoperatoren ein

    Warum? Was immer der Compiler automatisch generieren kann sollte man nicht selber machen.



  • @booster sagte in attempting to reference a deleted function:

    Vor c++ 11 wurde dann was gemacht?

    Es wurde kopiert. Wenn du move nicht ausdrücklich löscht, wird das auch weiterhin passieren.

    Mit C++17 und den Änderungen um guaranteed copy elision sollte es eigentlich wieder übersetzen.



  • @manni66 sagte in attempting to reference a deleted function:

    Warum? Was immer der Compiler automatisch generieren kann sollte man nicht selber machen.

    Keep calm and alt enter 🙂

    Wer resharper benutzt weiß wieso

    Resharper zeigt mir bei der Klasse die Meldung:

    Class 'CoordinatePointList' defines a default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move ssignment operator [cppcoreguildelines-special-member-functions]
    

    und bietet einem eben diese an direkt einzufügen.



  • @booster sagte in attempting to reference a deleted function:

    Wer resharper benutzt weiß wieso
    Resharper zeigt mir bei der Klasse die Meldung:

    Du verletzt also die 3er/5er/0er Regel. Dann ist dein Code bisher vermutlich falsch.



  • @manni66 sagte in attempting to reference a deleted function:

    Du verletzt also die 3er/5er/0er Regel. Dann ist dein Code bisher vermutlich falsch.

    ja bisher war immer nur der Konstruktor und der Destruktor implementiert



  • @manni66 sagte in attempting to reference a deleted function:

    Es wurde kopiert. Wenn du move nicht ausdrücklich löscht, wird das auch weiterhin passieren.

    ? -> ich habe doch move gelöscht. Dann wird er also weiterhin kopieren. Kompiler sagt aber trotzdem das move verwendet wird??

    Mit C++17 und den Änderungen um guaranteed copy elision sollte es eigentlich wieder übersetzen.

    "guaranteed copy elision" bedeutet was und wie aktiviere ich das? Ich nutze c++ 17 in Visual Studio 2017.



  • @booster sagte in attempting to reference a deleted function:

    @manni66 sagte in attempting to reference a deleted function:

    Es wurde kopiert. Wenn du move nicht ausdrücklich löscht, wird das auch weiterhin passieren.

    ? -> ich habe doch move gelöscht. Dann wird er also weiterhin kopieren. Kompiler sagt aber trotzdem das move verwendet wird??

    Du löschst den Movekonstruktor. Mach das nicht. Dann wird kopiert.

    Mit C++17 und den Änderungen um guaranteed copy elision sollte es eigentlich wieder übersetzen.

    "guaranteed copy elision" bedeutet was

    Google?



  • @manni66 sagte in attempting to reference a deleted function:

    Du löschst den Movekonstruktor. Mach das nicht. Dann wird kopiert.

    Aja. Ist das eine Trotzreaktion des Kompilers?
    "Ich nutze den Movekonstruktor nicht und kopiere. Wenn du aber den Movekonstruktor ausdrücklich löscht dann werde ich diesen verlangen"



  • Ja, so sind Compiler - wollen immer Recht haben und den Entwickler ärgern. 🙂



  • @booster sagte in attempting to reference a deleted function:

    Aja. Ist das eine Trotzreaktion des Kompilers?

    Nein. Du sagst explizit: move ist verboten. Dann versuchst du ein move. Wenn der Compiler nun sagen würde, "mach ich halt ein copy" würde er dein explizites Verbot doch umgehen.



  • @wob sagte in attempting to reference a deleted function:

    Nein. Du sagst explizit: move ist verboten. Dann versuchst du ein move. Wenn der Compiler nun sagen würde, "mach ich halt ein copy" würde er dein explizites Verbot doch umgehen.

    Das ist aber nicht das was manni beschreiben hat. Er meinte:

    "Du löschst den Movekonstruktor. Mach das nicht. Dann wird kopiert."



  • Doch, das widerspricht sich nicht mit Manni.

    Du kannst einen move-Konstruktor nicht haben, dann wird kopiert (das passiert, wenn du deinen move-Konstruktor einfach löscht). Oder du kannst einen move-Konstrukur haben und ihn explizit als verboten (=delete) markieren.

    Wenn du zum Beispiel einen benutzerdefinierten Copy-Konstruktor hast, dann wird KEIN automatischer move-Konstruktor erzeugt. Dann wird einfach kopiert statt zu moven.

    Wenn du aber einen move-Konstruktor deklarierst, wird der auch genommen und nicht auf copy ausgewichen. Du deklarierst einen move Konstruktor, also wird er auch genommen. Danach schaut der Compiler nach, ob er diesen auch nutzen darf. Mit =delete sagst du "nein". Also kompiliert es nicht.



  • Hi wob.

    Löschen und löschen sind zwei paar Stiefel.
    Also den gesamten Movekonstruktor aus dem Code löschen oder mit delete als "gelöscht" kennzeichnen.

    Aber dann ist der Satz doch von Manni falsch bzw. nicht ganz vollständig:
    "Es wurde kopiert. Wenn du move nicht ausdrücklich löscht, wird das auch weiterhin passieren."

    ausdrücklich gelöscht meint hier als = delete gekennzeichnet

    .... muss doch dann heißen:
    "Es wurde kopiert. Wenn du move nicht ausdrücklich löscht, aber auch nicht implementierst, wird das auch weiterhin passieren."

    Dann hätte ich das verstanden.


  • |  Mod

    @wob sagte in attempting to reference a deleted function:

    Wenn du aber einen move-Konstruktor deklarierst, wird der auch genommen und nicht auf copy ausgewichen.

    Fun fact: Ein Move-Konstruktor (oder Zuweisungsoperator) der implizit als deleted definiert wird, obwohl er als defaulted deklariert wurde, wird von overload resolution nicht berücksichtigt.