Verwenden des benutzerdefinierten std::set-Komparators
Lesezeit: 7 Minuten
Omry Jadan
Ich versuche, die Standardreihenfolge der Elemente in einer Reihe von Ganzzahlen so zu ändern, dass sie lexikographisch statt numerisch sind, und ich kann Folgendes nicht mit g++ kompilieren:
error: type/value mismatch at argument 2 in template parameter list for ‘template<class _Key, class _Compare, class _Alloc> class std::set’
error: expected a type, got ‘lex_compare’
Was mache ich falsch?
diralik
1. Moderne C++20-Lösung
auto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> s;
Wir verwenden die Lambda-Funktion als Komparator. Wie üblich sollte comparator einen booleschen Wert zurückgeben, der angibt, ob das als erstes Argument übergebene Element als vor dem zweiten in der Spezifizierung betrachtet wird strikte schwache Ordnung es definiert.
Muss in Beispiel 1 cmp an den Konstruktor übergeben werden? Wird der Satz selbst einen erstellen, da der Lambda-Typ als Vorlagentyp angegeben ist?
– PeteUK
10. Januar 2020 um 16:18 Uhr
@PeteUK vor dem C++20-Komparator muss an den Konstruktor übergeben werden. In C++20 kann ein Konstruktor ohne Argumente verwendet werden. Danke für die Frage; Antwort wurde aktualisiert
– diralik
11. Januar 2020 um 19:39 Uhr
@diralik Vielen Dank für die Antwort und Aktualisierung Ihrer bereits großartigen Antwort.
– PeteUK
11. Januar 2020 um 23:40 Uhr
Das 5. ist verrückt. Jeden Tag findet man neue Ecken und Winkel der Sprache.
– Jan Hošek
15. Juli 2020 um 13:04 Uhr
Zur akzeptierten Antwort befördert (nur 10+ Jahre nachdem die ursprüngliche Frage gestellt wurde!).
– Omry Yadan
24. Dezember 2020 um 4:47 Uhr
Yacoby
Sie verwenden eine Funktion, wo Sie einen Funktor verwenden sollten (eine Klasse, die den ()-Operator überlädt, damit sie wie eine Funktion aufgerufen werden kann).
Sie verwenden dann den Klassennamen als Typparameter
set<int64_t, lex_compare> s;
Wenn Sie den Funktor-Boilerplate-Code vermeiden möchten, können Sie auch einen Funktionszeiger verwenden (vorausgesetzt lex_compare ist eine Funktion).
@Omry: Mich würde interessieren, welchen Compiler Sie verwenden: codepad.org/IprafuVf
– Matthias N.
12. April 2010 um 9:21 Uhr
@Omry Welchen Compiler verwendest du?
– anon
12. April 2010 um 9:22 Uhr
@Omry Der C++-Standard besagt, dass der zweite Vorlagenparameter der Name eines Typs sein muss – ein Funktionsname ist nicht der Name eines Typs.
– anon
12. April 2010 um 9:24 Uhr
können wir decltype(lex_compare) verwenden, um den Funktionstyp zu bezeichnen?
– Lewis Chan
5. Juli 2018 um 4:13 Uhr
@LewisChan Der richtige Begriff wäre std::set<int64_t, decltype(&lex_compare)> s(&lex_compare)
– Nishant Singh
26. Oktober 2018 um 6:53 Uhr
Die Antwort von Yacoby inspiriert mich, einen Adapter zum Kapseln der Funktor-Boilerplate zu schreiben.
template< class T, bool (*comp)( T const &, T const & ) >
class set_funcomp {
struct ftor {
bool operator()( T const &l, T const &r )
{ return comp( l, r ); }
};
public:
typedef std::set< T, ftor > t;
};
// usage
bool my_comparison( foo const &l, foo const &r );
set_funcomp< foo, my_comparison >::t boo; // just the way you want it!
Wow, ich glaube, das war die Mühe wert!
Ansichtssache, denke ich.
– anon
12. April 2010 um 11:15 Uhr
Wow, in der Tat! Aber es zeigt auch, wie grässlich die C++-Syntax ist. Ich hoffe, dass zukünftige Versionen und Standards die C++-Syntax vereinfachen werden.
– Nav
10. Dezember 2020 um 13:02 Uhr
@Nav Das ::t Teil kann in C++14 eliminiert werden. Ich denke, es sieht ungefähr so gut aus wie das Java-Generika-Äquivalent und garantiert gleichzeitig keinen Laufzeitaufwand.
– Kartoffelklatsche
10. Dezember 2020 um 13:15 Uhr
Sie können einen Funktionskomparator verwenden, ohne ihn wie folgt zu umbrechen:
Dies ist irritierend, wenn Sie jedes Mal ein Set dieses Typs benötigen, und kann Probleme verursachen, wenn Sie nicht alle Sets mit demselben Komparator erstellen.
Ciro Santilli Путлер Капут 六四事
std::less<> bei der Verwendung von benutzerdefinierten Klassen mit operator<
Wenn Sie es mit einem Satz Ihrer benutzerdefinierten Klasse zu tun haben operator< definiert, dann können Sie einfach verwenden std::less<>.