Warum gibt es keinen Standard-Vergleichsoperator?



  • @SideWinder: leider nicht 😉

    erstens will ich für member mit überladenem operator== auch diesen ausführen, keinen bitweisen vergleich.
    zweitens darf C/C++ pad bytes zwischen den membern einführen (und macht das auch fleißig, um Daten an "gerade" Adressen zu bekommen), deren Wert undefiniert ist.



  • erstens will ich für member mit überladenem operator== auch diesen ausführen, keinen bitweisen vergleich.
    zweitens darf C/C++ pad bytes zwischen den membern einführen (und macht das auch fleißig, um Daten an "gerade" Adressen zu bekommen), deren Wert undefiniert ist.

    Das Problem mit den Padbytes ist leicht gelöst einfach ein

    memset(this,0,sizeof(*this));
    

    an den Anfang des Ctors.



  • peterchen schrieb:

    @Optimizer, SoM u.a.: Gibt genauso genügend Fälle, wo der Default-op= sinnlos ist. Die Argumente lassen sich alle auch auf einen operator== anwenden.

    Ist halt so (und "nur" ärgerlich, wenn man structs mit ein paar Dutzend Membern vergleichen muß), ich kann mir nur nicht vorstellen, das Bjarne einfach nicht dran gedacht hat 🤡

    Klar. Wie gesagt, ich finde eigentlich überhaupt keinen standardmäßig erzeugten Operator sinnvoll.



  • Warum nicht? So kann man sich bei PODs einiges an redundantem Code sparen.



  • Sowas saugt z.B.:

    private:
    	// Verboten!
    	Object(const Object&);
    	// Verboten!
    	const Object& operator=(const Object&);
    

    Jetzt hat man sogar noch nicht mal die Garantie, dass nicht doch die Operatoren genutzt werden, erst der Linker weint ganz sicher dann.

    Man sollte es zumindest abstellen können.



  • Ich will halt ganz einfach manchmal (meistens!) nicht, dass es diesen Operator und Copy-Konstruktor gibt und ich möchte verdammt nochmal das Recht haben, das selber zu entscheiden.
    Das ist echt ne krasse Einmischung in meine Kompetenzen. 😉



  • @Irgendwer:
    das memset geht schief, sobald du eine einzige virtuelle methode drin hast 😉

    @Optimizer:
    Mich stört auch der Mehraufwand, die "standards" abzuschalten. Man hat sich wahrscheinlich für die Variante entschieden, die ohne zusätzliche keywords auskommt.

    Die Möglichkeit, Member-weise operationen zu definieren ist aber schon hübsch, oder?



  • @Optimizer: Wenns dich stört, dann schreibe dir doch ein Makro, welches dir private Dummy-Opertoren da rein bastelt oder so.



  • Ähm, dann hab ich einen 1- statt 2- Zeiler. Wow. 😉

    Die Möglichkeit, Member-weise operationen zu definieren ist aber schon hübsch, oder?

    Ähm was meinst du jetzt genau?



  • peterchen schrieb:

    @Irgendwer:
    das memset geht schief, sobald du eine einzige virtuelle methode drin hast 😉

    Dann halt einfach in den operator new oder die ersten 4 Bytes auslassen.



  • @Irgendwer: Lustig...



  • MaSTaH schrieb:

    @Irgendwer: Lustig...

    War ja auch nie wirklich ernst gemeint 😉 .



  • ich geb nur ein stichwort: exception safety.

    ein standard op= ist nicht exception safe zu machen, hier ein beispiel:

    class foo{
            int i;
            std::vector<bar> j;
        public:   
    };
    

    gäbs hier nen standard op= würd er wahrscheinlich so aussehen:

    foo& operator=(const foo& Operand){
        i=Operand.i;
        j=Operand.j;
    }
    

    sieht ja auf dem ersten blick nich so schlimm aus oder?
    jetzt nehmen wir mal an, j wirft ne exception, weil der vector bei einer anstehenden erweiterung nichmehr genug platz allokieren kann(also new schlägt fehl).
    die exception geht an den benutzer raus,egal was der benutzer jetzt macht, der alte wert von i ist übrschrieben, und der alte wert von j wurde gelöscht.
    selbst wenn der vector exception sicher ist(was er ist), i bricht uns das genick, die klasse befindet sich in einem undefinierten zustand.



  • @otze: gutes Argument!
    Du kannst die betroffenen Daten aber in eine Basisklasse oder einen Member packen, und einen exception-sicheren operator= drumlegen. Ist zwar nicht immer eine Lösung, aber wenn man ein dutzend member hat...

    @Optimizer:
    Ich hatte bisher genügend Fälle, in denen bestimmte Operationen auf jeden member angewandt werden sollen. Wenn dir nicht gefällt das die "automatisch" da sind, kann ich das verstehen - aber gar nicht??? Vielleicht gefällt es Dir ja so besser:

    // nicht ganz echtes cpp
    class foo
    {
      // ... schön viele Member
      foo(foo const & rhs) memberwise;
      // operator= nicht definiert, also gibt es den nicht
    };
    

    oder noch hübscherer:

    // nicht ganz echtes cpp
    class foo
    {
      // ... schön viele Member
      foo & operator=(foo const & rhs)
      {
        try 
        {
          memberwise.operator=(rhs)
        }
        catch (...)
        {
          // now what to do? 
        }
      }
    };
    

    @Mastah: braucht man nicht mal macros dazu, geht mit entsprechenden membern bzw. basisklassen genauso:

    class nocopy
    {
      private:
        nocopy(nocopy const & rhs);
        nocopy & operator=(nocopy const & rhs);
    };
    
    class CMyClass : public nocopy
    {
       // oder auch: nocopy you_cannot_copyme;  
       // ... alles weitere
    };
    


  • peterchen schrieb:

    @otze: gutes Argument!
    Du kannst die betroffenen Daten aber in eine Basisklasse oder einen Member packen, und einen exception-sicheren operator= drumlegen. Ist zwar nicht immer eine Lösung, aber wenn man ein dutzend member hat...

    sicher kann man sonen op machen:

    foo& operator=(foo Operand){
        std::swap(*this,temp);
    }
    

    aber das wäre bei simpel klassen von der performance her absolute verschwendung...



  • Umm.. versteh ich nicht ganz...



  • otze schrieb:

    sicher kann man sonen op machen:

    foo& operator=(foo Operand){
        std::swap(*this,temp);
    }
    

    aber das wäre bei simpel klassen von der performance her absolute verschwendung...

    jo. evtl fehlt da noch

    foo temp(Operand);
    

    eigentlich sollte das der automatisch generierte sein. kommt ja vielleicht im c++-standard 2070 rein.
    simple typen, insbesondere pods kann der optimierer ja wieder raushauen. und wenn der typ fett genug ist, ne exception zu werfen, sind die kosten eher irrelevant.



  • peterchen schrieb:

    @Mastah: braucht man nicht mal macros dazu, geht mit entsprechenden membern bzw. basisklassen genauso:

    class nocopy
    {
      private:
        nocopy(nocopy const & rhs);
        nocopy & operator=(nocopy const & rhs);
    };
    
    class CMyClass : public nocopy
    {
       // oder auch: nocopy you_cannot_copyme;  
       // ... alles weitere
    };
    

    Ich finde es aber irgendwie unlogisch alle Klassen, deren Standard-Operatoren private sein sollen, von einer Basisklasse abzuleiten. Dann baue ich entweder jedes Mal Dummy-Operatoren per Copy&Paste rein oder ich bastle mir in irgendeinem versteckten Header ein hässliches Makro das die Arbeit für mich übernimmt, welches ich in eine private-Section reinhaue.

    // in ugly.hpp
    #define DENY_COPY(CLASS) CLASS(const CLASS&) {} \
                             CLASS& operator=(const CLASS&) { return *this }
    
    // in Class.hpp
    class Class
    {
      DENY_COPY(Class)
      int foo;
    public:
      Class::Class(int foo);
    };
    

    Ist ungetestet und ehrlich gesagt sieht es auch nicht so toll aus, aber wenn ich mir dadurch an vielen Stellen sinnlosen Code einsparen kann finde ich es nicht verwerflich. Ausser natürlich jemand nennt mir einen guten Grund, warum man es nicht so machen sollte oder zeigt mir eine bessere Lösung.

    Gruß,

    Christian

    EDIT: Also ich habe bisher nur kleinere Projekte mit 80-100 Klassen geschrieben. Ist es tatsächlich denkbar, dass man so viele Stellen hat wo ein Objekt nicht kopiert werden soll, dass sich eine - wie auch immer aussehende - Lösung als vereinfachend herausstellen würde?



  • wie gesagt, das gleiche bekommst du ja auch mit einem member hin, statt des Macros - mußt dem halt nur einen namen geben. (weiß nur nicht, ob der compiler den ohne speicher anlegen darf)

    Ich würd ein Macro nur verwenden, wenn es wirklich nicht anders geht.



  • peterchen schrieb:

    wie gesagt, das gleiche bekommst du ja auch mit einem member hin, statt des Macros - mußt dem halt nur einen namen geben. (weiß nur nicht, ob der compiler den ohne speicher anlegen darf)

    darf er nicht. aber dafür benutzt das makro netterweise mit #ifndef NDEBUG...
    also nullkosten in der release-version.


Anmelden zum Antworten