Lambda-Funktion in std::qsort


  • Mod

    @Swordfish sagte in Lambda-Funktion in std::qsort:

    @SeppJ sagte in Lambda-Funktion in std::qsort:

    Die Vergleichsfunktion bekommt keine Indizes übergeben, sondern die Werte.

    Mein Verständnis war, daß er die Werte in indices nach den Werten in myarray sortieren will, nicht myarray selbst.

    Ahh! Jetzt macht so einiges mehr Sinn.

    Nun, da das tatsächlich ein bisschen weniger trivial ist als nur std::sort aufzurufen machen wir es mal vor, wie es richtig geht:

    #include <algorithm>
    #include <iostream>
    #include <numeric>
    #include <vector>
    using namespace std;
    
    int main() {
    	vector<int> values = {7, 5, 8, 2, 6, 8, 10};
    	vector<int> indices(values.size());
    	iota(indices.begin(), indices.end(), 0);
    	cout << "vorher:\n";
    	for (auto i: indices)
    		cout << i << '(' << values[i] << ") ";
    	
    	std::sort(indices.begin(), indices.end(), 
                      [values](int a, int b) {return values[a] < values[b];}  );
    	
    	cout << "\nnachher:\n";
    	for (auto i: indices)
    		cout << i << '(' << values[i] << ") ";
    	
    	return 0;
    }
    

    Wie man sieht, ist nicht das Capture an sich problematisch, man muss es nur richtig machen. Man notiere auch den Vector und die Nutzung dazu passender Behilfsfunktionen für fehlerfreie und ausdrucksstarke Programmierung. Ohne das viele Rauschen für Casting von void und das Interface von qsort ist auch unmittelbar klar, was der Vergleichsausdruck erreichen soll.
    PS: https://ideone.com/6Sqgfo



  • @SeppJ sagte in Lambda-Funktion in std::qsort:

    Wie man sieht, ist nicht das Capture an sich problematisch, man muss es nur richtig machen.

    Würde man nicht [&values] capturen statt den vector zu kopieren?


  • Mod

    @wob sagte in Lambda-Funktion in std::qsort:

    @SeppJ sagte in Lambda-Funktion in std::qsort:

    Wie man sieht, ist nicht das Capture an sich problematisch, man muss es nur richtig machen.

    Würde man nicht [&values] capturen statt den vector zu kopieren?

    Ja, das wär 'ne Idee 🙂



  • Dann machst Du noch

    @SeppJ sagte in Lambda-Funktion in std::qsort:

    using namespace std;
    

    weg und alles wird gut.



  • Warum? Damit der Code dann so mit std:: verunstaltet und unleserlich ist, wie in deinem Code .txt Datei einlesen und in Vectoren speichern???



  • Ok, vielen Dank für die Erläuterungen. Das Beispiel mit std::qsort und Lambda-Funktion (ohne Capture) steht so sogar auf [https://en.cppreference.com/w/cpp/algorithm/qsort] . Mir war nicht klar, dass das hier nur für den Spezialfall ohne Capturing funktioniert, die Verwendung der Lambda-Syntax im Beispiel statt eines "normalen" C-Funktionspointers ist da schon verwirrend.
    Diese Einschränkung betrifft dann also alle Funktionen, die ursprünglich von C kommen und in <cstdlib> statt <algorithm> stehen?


  • Mod

    Um das mal genauer zu erklären: Ein Lambda ohne Capture ist nur eine ganz normale Funktion, die mit etwas anderer Syntax definiert wurde und keinen Namen hat. Daher gibt es eine ganz natürliche und automatische Konvertierung von solchen Lambdas hin zu einem Zeiger auf diese Funktion.

    Ein Lambda mit Capture ist hingegen ein funktionsartiges Objekt. Das trägt einen inneren Zustand (nämlich den Kontext, den es captured). Ein Verweis auf solch ein Lambda kann kein einfacher Funktionszeiger mehr sein, denn es muss sowohl irgendwie der Code als auch sein Kontext referenziert werden, wenn man das Lambda aufrufen möchte. Früher hat man solche funktionsartigen Objekte oft "Functor" genannt, aber heutzutage ist das halt einfach ganz normal und selbstverständlich in C++, dass Funktionen auch funktionsartige Objekte sein können, daher ist es lange her, dass ich jemanden gehört habe, der auf die Unterscheidung Wert legt. .

    Bei der C-Schnittstelle von qsort ist diese Unterscheidung dann aber doch wichtig. Denn die kann nur C-Funktionszeiger und ist überfordert mit allem was anders ist. Daher hat man früher auch mehr von Functors geredet, weil es da noch häufiger solche Schnittstellen gab und es noch neu war, dass man einer modernen Schnittstelle auch etwas anderes mitgeben kann. Entsprechend kommt std::sort damit ganz selbstverständlich klar, man muss nichts spezielles dafür tun, siehe mein Code.

    Du darfst davon ausgehen, dass alle der C-Funktionen solche Macken haben, wenn es um Zusammenarbeit mit C++ geht. Die sind zur Abwärtskompatibilität da, und sollten andernfalls nicht benutzt werden. Wenn man sich ganz genau auskennt, dann kann man ihnen vorsichtig beibringen, mit ausgewählten C++-Strukturen zusammen zu arbeiten. Hier hast du versucht, einen Vector zu sortieren, und das hätte auch funktioniert. Ich hoffe, du weißt auch warum das funktioniert hätte, denn zum Beispiel eine deque (die nahezu das gleiche Interface hat wie ein vector) hätte nicht funktioniert. Und auch ein Lambda hätte funktioniert, solange es kein Capture gehabt hätte.



  • @Lutex sagte in Lambda-Funktion in std::qsort:

    Diese Einschränkung betrifft dann also alle Funktionen, die ursprünglich von C kommen und in <cstdlib> statt <algorithm> stehen?

    Um es noch mehr auf den Punkt zu bringen: Alle Funktionen (egal woher) die "nur" einen Function Pointer nehmen kannst Du nicht mit einem Lambda füttern das captured (grausiges Wort).



  • Ok, jetzt hab ich's verstanden. Herzlichen Dank nochmal für Eure Mühe!



  • @Swordfish

    ... captured (grausiges Wort)

    Du kannst ja "kapturieren" statt dessen verwenden 😆


Log in to reply