Wie kann ich std::maps mit benutzerdefinierten Typen als Schlüssel verwenden?

Lesezeit: 1 Minute

Wie kann ich stdmaps mit benutzerdefinierten Typen als Schlussel verwenden
Unbekannt

Ich frage mich, warum ich STL-Maps nicht mit benutzerdefinierten Klassen verwenden kann. Wenn ich den folgenden Code kompiliere, erhalte ich die folgende kryptische Fehlermeldung. Was heißt das? Warum passiert es auch nur mit benutzerdefinierten Typen? (Primitive Typen sind in Ordnung, wenn sie als Schlüssel verwendet werden.)

C:\MinGW\bin..\lib\gcc\mingw32\3.4.5……..\include\c++\3.4.5\bits\stl_function.h||In Mitgliedsfunktion `bool std:: less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp =
Class1]’:|

C:\MinGW\bin..\lib\gcc\mingw32\3.4.5……..\include\c++\3.4.5\bits\stl_map.h|338|instanziiert von `_Tp& std:: map<_Key, _Tp, _Compare, _Alloc>::Operator[](const _Key&) [with _Key = Class1, _Tp = int, _Compare = std::less, _Alloc = std::allocator >]’|

C:\Benutzer\Admin\Dokumente\dev\sandbox\sandbox\sandbox.cpp|24|von hier aus instanziiert|

C:\MinGW\bin..\lib\gcc\mingw32\3.4.5……..\include\c++\3.4.5\bits\stl_function.h|227|error: no match for ‘operator <' in '__x < __y'| ||=== Bau abgeschlossen: 1 Fehler, 0 Warnungen ===|

#include <iostream>
#include <map>

using namespace std;

class Class1
{
public:
    Class1(int id);

private:
    int id;
};

Class1::Class1(int id): id(id)
{}

int main()
{
    Class1 c1(1);

    map< Class1 , int> c2int;
    c2int[c1] = 12;

    return 0;
}

  • Mögliches Duplikat von C++ unordered_map mit einem benutzerdefinierten Klassentyp als Schlüssel

    – Ciro Santilli Путлер Капут 六四事

    18. April 2019 um 20:17 Uhr

Wie kann ich stdmaps mit benutzerdefinierten Typen als Schlussel verwenden
Pavel Minaev

Du nicht verfügen über definieren operator< eigentlich für deine Klasse. Sie können auch eine Komparatorfunktionsobjektklasse dafür erstellen und diese zum Spezialisieren verwenden std::map. Um dein Beispiel zu erweitern:

struct Class1Compare
{
   bool operator() (const Class1& lhs, const Class1& rhs) const
   {
       return lhs.id < rhs.id;
   }
};

std::map<Class1, int, Class1Compare> c2int;

Es passiert einfach so, dass die Voreinstellung für den dritten Template-Parameter von std::map ist std::lessdie an delegieren wird operator< für Ihre Klasse definiert (und schlagen fehl, wenn es keine gibt). Aber manchmal möchten Sie, dass Objekte als Kartenschlüssel verwendet werden können, aber Sie haben eigentlich keine sinnvoll Vergleichssemantik, und deshalb möchten Sie die Leute nicht verwirren, indem Sie angeben operator< in deiner Klasse nur dafür. Wenn das der Fall ist, können Sie den obigen Trick anwenden.

Eine weitere Möglichkeit, dasselbe zu erreichen, ist die Spezialisierung std::less:

namespace std
{
    template<> struct less<Class1>
    {
       bool operator() (const Class1& lhs, const Class1& rhs) const
       {
           return lhs.id < rhs.id;
       }
    };
}

Das hat den Vorteil, dass es abgeholt wird std::map “standardmäßig”, und doch belichten Sie nicht operator< andernfalls zum Client-Code.

  • Ich würde vorschlagen, den beiden Funktionen ein const-Schlüsselwort hinzuzufügen.

    – Diomidis spinellis

    28. August 2010 um 12:41 Uhr

  • vielleicht lohnt es sich friend mit dem struct less sonst sehe ich es als eine kompromittierte Kapselung.

    – Skywalker

    3. Oktober 2012 um 14:52 Uhr

  • Die Schablonenstruktur sollte mit einem Semikolon abgeschlossen werden, andernfalls erhalten Sie Kompilierungsfehler. Leider konnte ich dies aufgrund der geringen Anzahl geänderter Charaktere nicht mit einem Edit beheben

    – Ident

    26. Juli 2015 um 22:09 Uhr

  • Aber warum setzen Sie struct less info std?

    – Wladimir Zyschnati

    18. Juli 2016 um 11:58 Uhr

  • Es ist schon drin std. Das ist nur eine Spezialisierung davon.

    – Pavel Minaev

    18. Juli 2016 um 18:02 Uhr

1647090011 365 Wie kann ich stdmaps mit benutzerdefinierten Typen als Schlussel verwenden
GManNickG

Standardmäßig std::map (und std::set) verwenden operator< Sortierung zu bestimmen. Daher müssen Sie definieren operator< auf deine Klasse.

Es werden zwei Objekte betrachtet gleichwertig if !(a < b) && !(b < a).

Wenn Sie aus irgendeinem Grund einen anderen Komparator verwenden möchten, wird das dritte Vorlagenargument der map kann geändert werden, zu std::greaterzum Beispiel.

  • Tatsächlich können Sie den Komparator in fast jede Funktion mit zwei Argumenten ändern.

    – xtofl

    9. Juli 2009 um 10:07 Uhr

1647090011 140 Wie kann ich stdmaps mit benutzerdefinierten Typen als Schlussel verwenden
aJ.

Sie müssen definieren operator < für die Klasse1.

Map muss die Werte mit dem Operator < vergleichen und daher müssen Sie dasselbe angeben, wenn benutzerdefinierte Klassen als Schlüssel verwendet werden.

class Class1
{
public:
    Class1(int id);

    bool operator <(const Class1& rhs) const
    {
        return id < rhs.id;
    }
private:
    int id;
};

  • Es braucht keinen operator< ; es ist lediglich standardmäßig darauf eingestellt. Siehe die Antwort von GMan oder Pavel.

    – xtofl

    9. Juli 2009 um 10:08 Uhr

1647090012 55 Wie kann ich stdmaps mit benutzerdefinierten Typen als Schlussel verwenden
Kaushal

class key
{
    int m_value;
public:
    bool operator<(const key& src)const
    {
        return (this->m_value < src.m_value);
    }

};
int main()
{
    key key1;
    key key2;
    map<key,int> mymap;
    mymap.insert(pair<key,int>(key1,100));
    mymap.insert(pair<key,int>(key2,200));
    map<key,int>::iterator iter=mymap.begin();
    for(;iter!=mymap.end();++iter)
    {
        cout<<iter->second<<endl;
    }


}

Schlüssel müssen vergleichbar sein, aber Sie haben keinen passenden definiert operator< für Ihre benutzerdefinierte Klasse.

Ich möchte ein wenig erweitern Pavel Minaevs Antwort, die Sie lesen sollten, bevor Sie meine Antwort lesen. Beide von Pavel vorgestellten Lösungen werden nicht kompiliert, wenn das zu vergleichende Element (wie z id im Code der Frage) ist privat. In diesem Fall wirft VS2013 den folgenden Fehler für mich:

Fehler C2248: „Class1::id“: Zugriff auf privates Mitglied, das in Klasse „Class1“ deklariert ist, nicht möglich

Wie von erwähnt SkyWalker in den Kommentaren zu Pavels Antwort mit a friend Deklaration hilft. Wenn Sie sich über die korrekte Syntax wundern, hier ist sie:

class Class1
{
public:
    Class1(int id) : id(id) {}

private:
    int id;
    friend struct Class1Compare;      // Use this for Pavel's first solution.
    friend struct std::less<Class1>;  // Use this for Pavel's second solution.
};

Code auf Ideone

Wenn Sie jedoch beispielsweise eine Zugangsfunktion für Ihr privates Mitglied haben getId() zum idwie folgt:

class Class1
{
public:
    Class1(int id) : id(id) {}
    int getId() const { return id; }

private:
    int id;
};

dann können Sie es anstelle von a verwenden friend Deklaration (d. h. Sie vergleichen lhs.getId() < rhs.getId()). Seit C++11Sie können auch a verwenden Lambda-Ausdruck zum Pavels erste Lösung, anstatt eine Komparatorfunktionsobjektklasse zu definieren. Alles zusammengenommen könnte der Code wie folgt geschrieben werden:

auto comp = [](const Class1& lhs, const Class1& rhs){ return lhs.getId() < rhs.getId(); };
std::map<Class1, int, decltype(comp)> c2int(comp);

Code auf Ideone

1647090012 386 Wie kann ich stdmaps mit benutzerdefinierten Typen als Schlussel verwenden
Benutzer541686

Ihr Beispiel funktioniert in C++20 wenn Sie nur hinzufügen

auto operator<=>(Class1 const &) const = default;

zu deiner Klasse.

993670cookie-checkWie kann ich std::maps mit benutzerdefinierten Typen als Schlüssel verwenden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy