[Gelöst] Designfrage - Vergleichsoperator mit Containern als Member



  • Hallo zusammen (mal wieder),

    Gehen wir mal von folgender Klasse aus:

    class Name_pairs {
    public:
        friend bool operator==//...
    
    private:
        vector<string> names;
        vector<double> ages;
    };
    

    Ich möchte nun den Vergleichsoperator für die Klasse definieren.

    Ich denke es ist halbwegs logisch, dass wenn zwei Name_pairs gleich sind,
    sie exakt die gleichen Namen enthalten, deren Alter auch exakt gleich ist.
    Sollte ich mich da irren, dann müsste ich dieses Problem nochmal anders angehen.

    Jetzt kanns aber ja sein, dass zwei Name_pairs zwar die gleichen Elemente besitzen, jedoch in anderer Reihenfolge:

    // 1
    Foo: 15
    Bar: 25
    
    //2
    Bar: 25
    Foo: 15
    

    So würde folgender naiver Vergleichsoperator für obiges Beispiel false ergeben:

    bool operator==(const Name_pairs& lhs, const Name_pairs& rhs)
    {
        for (std::size_t i = 0; i < lhs.names.size(); ++i) {
            if (lhs.names[i] != rhs.names[i] || lhs.ages[i] != rhs.ages[i]) 
                return false;
        }
        return true;
    }
    

    Es müssten also, damit etwas dieser Art funktioniert, erst die Elemente der jeweiligen Name_pairs Klassen sortiert werden. Wie es der Zufall so will, hab ich auch schon eine sort() Memberfunktion dafür.

    Da stellt sich aber folgendes Problem:
    Mein Vergleichsoperator müsste entweder:
    [1] Die Elemente, die verglichen werden sollen, eventuell verändern (klingt ziemlich übel)
    [2] Die Elemente Kopieren (2 vectors ? Klingt irgendwie auch übel)

    Nun meine Frage an euch:
    Gibt es da Abhilfe, wie ich das elegant, korrekt, usw. lösen könnte?
    Oder muss ich mich nun damit abfinden, dass es eben false heißt, wenn alles außer die Reihenfolge übereinstimmt.

    Mir ist im Übrigen schon klar, dass halt der Nutzer der Name_pairs Klassen halt vor dem Vergleich selbst sort() aufrufen könnte/sollte (angenommen das wäre jetzt kein triviales Beispiel). Ihr wisst aber bestimmt wie das ist - sowas ist Fehleranfällig und wird gerne vergessen.

    Dann halt noch frohe Weihnachten, schöne Feiertage, ...

    LG



  • Das Problem ist, dass deine Member nicht die wahren Daten wiederspiegeln.

    Bei einem vector ist die Reihenfolge eine wichtige Eigenschaft und doppelte Elemente sind ganz natürlich.

    Wenn du einfach eine ungeordnete Liste halten möchtest, dann nimm als Member

    unordered_map<string, int> name_to_age;
    

    oder so.

    Falls du vector aus gutem Grund verwendest, kommst du halt nicht darum, händisch aktiv zu werden und eine langsamere Laufzeit in Kauf zu nehmen.

    Kurze Antwort: Dieses Problem tritt bei gut designten Klassen überhaupt nicht auf.



  • Hat das einen tieferen Sinn, dass das nach einer map aussieht, aber keine map ist?



  • Es gibt zwei Probleme:
    1. Verwendung einer Struct of Arrays: Ein Element im Vektor names korrespondiert zu einem Element im Vektor ages. Zusammen bilden sie einen Typen. Dann schreib das auch so.

    std::vector<std::pair<std::string, double>> name_age_pairs;
    

    2. Verwendung eines vectors, wenn semantisch etwas anderes gefordert ist. Bei einem vector ist die Reihenfolge wichtig. Bei dir nicht. Du hast einfach nur eine Ansammlung von Werten. Aka eine Menge. Aka ein std::set/std::unordered_set:

    std::unordered_set<std::pair<std::string, double>> name_age_pairs;
    

    Den Vergleichsoperator kannst du nun trivial einfach schreiben:

    bool operator==(const Name_pairs& lhs, const Name_pairs& rhs)
    {
        return lhs.name_age_pairs == rhs.name_age_pairs;
    }
    

    Fertig.

    Edit: Interessant, ich habe das als Menge interpretiert, andere als Map. Wenn du das Alter für einen bestimmten Name brauchst, brauchst du eine Map. Wenn du beide als eine Einheit betrachtest, eine Menge.



  • Nathan schrieb:

    Edit: Interessant, ich habe das als Menge interpretiert, andere als Map. Wenn du das Alter für einen bestimmten Name brauchst, brauchst du eine Map. Wenn du beide als eine Einheit betrachtest, eine Menge.

    Ich habe mir gedacht, wenn doppelte Namen auftreten, hat man eh ein Problem und kann den Vergleichsoperator nicht sinnvoll defniieren. Also nimmt man an, dass der Name eindeutig ist.
    Hängt aber natürlich von der Situation ab.



  • Ich vergaß zu erwähnen, ich sehe das ebenfalls so, dass dieses Design sehr blöd ist. Es stand halt in der Aufgabe, dass man das mit zwei vector (string und double) so speichern soll. Auch z.B. sie sort() Methode würde bei richtigem Design einfacher werden.

    Ich hab mir mal die Vorschläge zu Herzen genommen und etwas recherchiert.
    Mit set bzw. unordered_set hatte ich bis jetzt noch nicht viel am Hut, ich sollte mal meine Bücher fertig lesen :s

    Macht das jetzt eigentlich einen semantischen Unterschied, ob ich

    unordered_map<string, int> name_to_age;
    

    oder

    std::unordered_set<std::pair<std::string, double>> name_age_pairs;
    

    verwende?
    Edit: Oh, ich sehe gerade, dass die Posts aktualisiert wurden, somit hat sich das auch geklärt.

    Soweit schonmal danke, das ging ja fix. Weiß garnicht, wieso ich mir über solche blöden Aufgaben den Kopp zerbreche...

    Ich werd versuchen nicht noch weitere dämlichen Beispiele mit Name Alter Paaren zu bringen 😃


Log in to reply