Probleme mit std::sort, std::ranges::sort und eigenem < Operator



  • @john-0 sagte in Probleme mit std::sort, std::ranges::sort und eigenem < Operator:

    Du musst den neuen operator<=> definieren! Dann übersetzt das auch problemlos.

    Ja, das sollte man auch beachten.



  • Ok, der Compiler mag mich überhaupt nicht. Wenn ich Folgendes

    #include <algorithm>
    #include <vector>
    #include <iostream>
    
    class Foo {
            int value;
    public:
            explicit Foo (const int v) : value(v) {}
    
            auto operator<=> (const Foo& rhs) const = default;
    
            int get() const {
                    return this->value;
            }
    };
    
    int main() {
        std::vector<Foo> vec = { Foo{5}, Foo{1}, Foo{9}, Foo{3} };
    
        std::ranges::sort(vec);
    
        for (const auto& f : vec) {
            std::cout << f.get() << ' ';
        }
        std::cout << "\n";
    }
    

    eingebe übersetzt er es problemlos, und es gibt ein richtiges Ergebnis. Wenn ich versuch den operator<=> selbst zu schreiben, wirft er mir einen Fehler. Momentan hadere ich damit, dass ich das nicht nachvollziehen kann.



  • @john-0

    dass der Compiler bei deinem selbstgeschriebenen operator<=> streikt, liegt an den strengen Concepts von C++20 Ranges.
    Damit ein Typ mit std::ranges::sort sortiert werden kann, muss er das Concept std::sortable erfüllen. Dieses baut wiederum auf std::totally_ordered auf. Das bedeutet für dich: Ein Typ ist für Ranges nur dann sortierbar, wenn er sowohl eine Ordnung (<) als auch eine Gleichheit (==) besitzt.

    Warum funktioniert = default?

    Wenn du auto operator<=>(const Foo&) const = default; schreibst, ist der Compiler so nett und generiert für dich implizit auch gleich den passenden Gleichheitsoperator (operator==) mit. Damit ist die Bedingung für std::totally_ordered erfüllt.

    Warum scheitert die manuelle Version?

    Wenn du den operator<=> von Hand schreibst, generiert der Compiler den operator== nicht automatisch. std::ranges::sort sieht also eine Klasse, die man zwar vergleichen, aber nicht auf Gleichheit prüfen kann – und bricht mit einem kryptischen Compiler-Fehler ab.

    Die Lösung

    Wenn du den Spaceship-Operator selbst implementierst, musst du den operator== entweder ebenfalls manuell schreiben oder ihn separat via = default anfordern:

    class Foo {
        int value;public:
        explicit Foo(const int v) : value(v) {}
    
        // 1. Dein manueller Spaceship-Operator für die Ordnung
        auto operator<=>(const Foo& rhs) const {
            return this->value <=> rhs.value;
        }
    
        // 2. WICHTIG FÜR RANGES: Der Gleichheitsoperator (kann defaulted werden)
        bool operator==(const Foo& rhs) const = default; 
    
        int get() const { return this->value; }
    };
    

    Sobald der operator== vorhanden ist, kompiliert dein Code auch mit der manuellen Variante fehlerfrei.



  • @Anon-81275 sagte in Probleme mit std::sort, std::ranges::sort und eigenem < Operator:

    Warum scheitert die manuelle Version?

    Wenn du den operator<=> von Hand schreibst, generiert der Compiler den operator== nicht automatisch. std::ranges::sort sieht also eine Klasse, die man zwar vergleichen, aber nicht auf Gleichheit prüfen kann – und bricht mit einem kryptischen Compiler-Fehler ab.

    Aua, der operator<=> wurde doch eingeführt, weil er eben direkt „Kleiner als“, „Gleichheit“ und „Größer als“ ausdrücken kann, und da muss man explizit die Gleichheit nochmals implementieren? Kein Wunder, wenn C++ immer ins Hintertreffen gerät – das ist unlogisch. Früher wusste man, dass operator== und operator< zu implementieren ist.

    Danke für den Hinweis.



  • Ja, ich bin auch kein C++-"Stammspieler", aber ich sehe das so ähnlich wie du. Ich glaube, das ist so etwas ähnliches wie Ballast, der einfach die Komplexität erhöht. Mir fällt jedenfalls spontan kein sinnvoller Use-Case ein.



  • Danke Leute, ich habe die Operatoren <=> und == definiert und damit funktioniert das std::ranges::sort auch.



  • @john-0 sagte in Probleme mit std::sort, std::ranges::sort und eigenem < Operator:

    Kein Wunder, wenn C++ immer ins Hintertreffen gerät – das ist unlogisch.

    Uff, ehrlich gesagt juckt mich das nicht.

    Das muss man wissen, man könnte es auch als eine Macke bezeichnen, aber die Schönheit eine Sprache ist eine Sache, die Stilblüten der Benutzer eine andere.

    Ich sehe da gerade eine Design-Pattern Seuche.

    class UART_Base
    {
    
    }
    class UART_1 : public UART_Base
    {
      UART_1* GetInstance()...
    }
    
    UART_BASE* GetInstance(int Mode)
    {
       if (Mode == 1)
          return UART_1.GetInstance();
      if (mode == 2)
         return UART_2.GetInstance();
    }
    

    Und gerade sehe ich in der Android Welt so eine schöne Mischung aus Java und Kotlin. Gradle ist so lahm wie eh und je. Man schießt gerne mit Kanonen auf Spatzen in dem man eine z.B. eine Kommunikation IO in einen eigenen Prozess auslagert und mittels Sockets darauf zugreift.



  • @Quiche-Lorraine sagte in Probleme mit std::sort, std::ranges::sort und eigenem < Operator:

    die Stilblüten der Benutzer eine andere.

    ... für mich ist das eine pauschale Abwertung. Es hat doch nicht mehr mit Geschmack zu tun, wenn eine Sprache derart verschlimmbessert wird, dafür gibt es sogar objektive Kriterien bzw. Metriken.

    Wenn es für ein Feature keinen Mehrwert gibt, aber ich nun für das gleiche Ergebnis mehr als vorher schreiben muss, oder Bestandscode unnötig legacy wird, dann verfehlt das Feature den Zweck. Also, höherer Aufwand, gleicher Nutzen.

    Ja, nicht jeder mag Rindfleisch, aber deshalb leben Vegetarier auch nicht gesünder.

    Sorry, musste das einmal rauslassen.



  • @john-0 Es gibt Fälle, bei denen Ordering und Gleichheit nicht wirklich passend sind.
    Da ich gerade nicht am Computer bin, nur ein Link anstelle einer Erklärung: https://iifx.dev/en/articles/457613117/mastering-c-20-comparisons-aligning-spaceship-operator-and-equality-for-your-point-class



  • @Schlangenmensch 503 Fehler, wenn ich auf den Link klicke...

    Ich verstehe nicht, in welchen Fällen man den Kontrakt der Quasiordnungsrelation (Reflexivität und Transitivität) aufgeben sollte - also wann es sinnvoll wäre, dass a < b und b > a, a != b nicht gälte.


Anmelden zum Antworten