eigene Vergleichsfunktion für list.sort() schreiben



  • Hallo,

    ich habe wie in meinem Buch beschrieben versucht eine ganz einfache Vergleichsfunktion zu schreiben, die ich an Stelle der Standardvergleichfunktion bei der Sortierung einer Liste benutzen will. Zur Zeit macht diese Funktion nicht anderes als die Standardfunktion, aber sie ist ja auch nur zum testen dar und soll dann durch eine andere ersetzt werde.
    Mein Code sieht wie folgt aus:

    #include <iostream>
    #include <list>
    using namespace std;
    
    struct mycompare
    {
    	bool operator() (const int &a, const int &b)
    	{
    		return (a < b);
    	}
    };
    
    int main()
    {
    	list<int> listInt;
    	for(int i = 0; i <10; i++)
    		listInt.push_back(i);
    	listInt.sort(mycompare());
    	return 0;
    }
    

    Leider bekomme ich immer einer Fehlermeldung:

    error C2664: 'void __thiscall std::list<int,class std::allocator<int> >::sort(struct std::greater<int>)' : Konvertierung des Parameters 1 von 'struct mycompare' in 'struct std::greater<int
    >' nicht moeglich
            Quelltyp konnte von keinem Konstruktor angenommen werden, oder die Ueberladungsaufloesung des Konstruktors ist mehrdeutig
    Fehler beim Ausführen von cl.exe.
    

    Was habe ich falsch gemacht?



  • beccy schrieb:

    Was habe ich falsch gemacht?

    Nichts. Der Fehler liegt bei deinem Compiler bzw. deiner Standard-Bibliothek (eine veraltete Dinkumware-Implementation). list::sort ist normalerweise ein Membertemplate. Deine Standard-Lib unterstützt nun aber keine Membertemplates.

    Du hast jetzt zwei möglichkeiten:
    1. Andere Standard-Lib einsetzen. Z.B. STLPort
    2. Für den Elementtyp deiner List: op< implementieren oder std::greater spezialisieren.



  • HumeSikkins schrieb:

    2. Für den Elementtyp deiner List: op< implementieren oder std::greater spezialisieren.

    Warum std::greater? std::less würde doch mehr Sinn machen.



  • Also wenn ich std::greater überlade, dann funktioniert es.
    Das Problem ist nur, dass die Inhalte meiner Liste Pointer auf meine Template-Klasse sind. Leider habe ich es nicht hinbekommen die std::greater so umzumodeln, dass ich meine Template-Klasse einsetzten konnte:

    template<class S>
    struct std::greater<Node<S>*> : binary_function<Node<S>*, Node<S>*, bool> {
    bool operator()(Node<S>* x, Node<S>* y) const {
    	cout << "halle";
    return (x->GetOrdNr() > y->GetOrdNr());
    }
    };
    
    error C2988: Unerkannte Vorlagendeklaration/-definition
    

    D.h. dann wohl, dass ich für jeden möglichen Datentyp die std::greater überschreiben müsste. Um dies zu verhindern, wollte ich einfach den operator> überladen, aber da ich Zeiger in meiner Liste habe ruft er den überladenen Operator nicht auf (würde er nur machen, wenn ich die Zeiger derefenziere und dafür müsste ich ja dann wieder die std::greater überschreiben).
    Gibt es eine Möglichkeit, auch über einen Zeiger den richtigen Operator aufzurufen, oder meine Template-Klasse in die std::greater einzubringen?



  • Shlo schrieb:

    HumeSikkins schrieb:

    2. Für den Elementtyp deiner List: op< implementieren oder std::greater spezialisieren.

    Warum std::greater? std::less würde doch mehr Sinn machen.

    Da musst du die Dinkumware-Leute Fragen. Die haben das Membertemplate

    template<class Pred>
    void sort(Pred pr);
    

    durch die Methode

    void sort(greater<T> pr);
    

    ersetzt. Damit ist man dann eindeutig auf greater festgelegt 🙂

    Das Problem ist nur, dass die Inhalte meiner Liste Pointer auf meine Template-Klasse sind. Leider habe ich es nicht hinbekommen die std::greater so umzumodeln, dass ich meine Template-Klasse einsetzten konnte:

    Dein Compiler unterstützt leider keine partielle Spezialisierung Template-Klassen.

    Um dies zu verhindern, wollte ich einfach den operator> überladen

    Wenn du Zeiger vergleichen willst geht das leider nicht so einfach, da du für das Überladen von Operatoren ein UDT brauchst. Ein Zeiger ist aber kein UDT. Du kannst also keinen op> für zwei Zeiger überladen.

    Spontan sehe ich zwei Möglichkeiten:
    1. Du baust dir ein Makro für die Spezialisierung von std::greater und umgehst damit die lästige Schreibarbeit.

    #define NODE_PTR_GREATER(Type) \
    	template <>\
    	struct std::greater<Node<Type>*> : std::binary_function<Node<Type>*, Node<Type>*, bool> { \
    		bool operator()(Node<Type>* x, Node<Type>* y) const { \
    			return (x->GetOrdNr() > y->GetOrdNr());	\
    		}	\
    	};
    

    2. Du packst in deine Liste nicht Pointer sondern Pointer-Wrapper (Smart-Pointer). Minimal:

    template <class T>
    struct NodePtr
    {
    	NodePtr(Node<T>* p)
    		: ptr_(p)
    	{}
    
    	operator Node<T>* () 
    	{
    		return ptr_;
    	}
    	// ...
    	Node<T>* ptr_;
    };
    

    Dann überlädst du einen operator < für diese Klasse:

    template <class T>
    bool operator<(const NodePtr<T>& lhs, const NodePtr<T>& rhs)
    {
    	// ...
    }
    


  • HumeSikkins schrieb:

    Da musst du die Dinkumware-Leute Fragen. Die haben das Membertemplate

    template<class Pred>
    void sort(Pred pr);
    

    durch die Methode

    void sort(greater<T> pr);
    

    ersetzt. Damit ist man dann eindeutig auf greater festgelegt 🙂

    Das ist mir gar nicht aufgefallen 🤡



  • Danke HumeSikkins,

    werde das mal ausprobieren.


Anmelden zum Antworten