Gibt es einen Grund Standardalgorithmen nehmen Lambdas nach Wert? [Duplikat]

8

Also habe ich hier eine Frage gestellt: Lambda funktioniert auf dem neuesten Visual Studio, funktioniert aber nicht anderswo , auf die ich habe die Antwort bekommen, dass mein Code eine Implementierung ist, da der Standard 25.1 [algorithms.general] 10 sagt:

  

Wenn nicht anders angegeben, dürfen Algorithmen, die Funktionsobjekte als Argumente verwenden, kopieren   diese Funktionen Objekte frei. Programmierer, für die die Objektidentität wichtig ist, sollten in Erwägung ziehen, a   Wrapperklasse, die auf ein nicht kopiertes Implementierungsobjekt wie reference_wrapper<T>

verweist

Ich möchte nur einen Grund, warum das passiert? Uns wird gesagt, dass unser gesamtes Leben Objekte als Referenz nimmt, warum nimmt dann der Standard Funktionsobjekte nach Wert und noch schlimmer in meiner verknüpften Frage Kopien dieser Objekte? Gibt es einen Vorteil, dass ich nicht verstehe, es so zu machen?

    
Jonathan Mee 12.12.2016, 19:46
quelle

2 Antworten

6

std setzt Funktionsobjekte voraus und Iteratoren können frei kopiert werden.

std::ref stellt eine Methode bereit, um ein Funktionsobjekt in eine Pseudoreferenz mit einer kompatiblen operator() zu verwandeln, die eine Referenz- statt eine Wertesemantik verwendet. Also nichts von großem Wert ist verloren.

Wenn dir in deinem ganzen Leben beigebracht wurde, Gegenstände durch Bezugnahme zu nehmen, überlege es dir. Wenn es keinen guten Grund gibt, nehmen Sie Objekte nach Wert. Das Nachdenken über Werte ist viel einfacher; Verweise sind Zeiger auf jeden Zustand in Ihrem Programm.

Die herkömmliche Verwendung von Referenzen als Zeiger auf ein lokales Objekt, auf das keine andere aktive Referenz in dem Kontext verweist, in dem es verwendet wird, kann nicht jemand, der Ihren Code liest, noch den Compiler vermuten lassen. Wenn Sie auf diese Weise über Referenzen sprechen, fügen sie Ihrem Code keine lächerliche Menge an Komplexität hinzu.

Aber wenn Sie so über sie nachdenken, werden Sie Fehler haben, wenn Ihre Annahme verletzt wird, und sie werden subtil, grob, unerwartet und schrecklich sein.

Ein klassisches Beispiel ist die Anzahl von operator= , die bricht, wenn this und das Argument sich auf dasselbe Objekt beziehen. Aber jede Funktion, die zwei Referenzen oder Zeiger des gleichen Typs benötigt, hat das gleiche Problem.

Aber selbst eine Referenz kann Ihren Code brechen. Schauen wir uns sort an. Im Pseudocode:

%Vor%

Lassen Sie uns jetzt eine Referenz machen:

%Vor%

Wie wäre es mit diesem?

%Vor%

Rufen Sie jetzt sort( begin(vector), end(vector), alice ) auf.

Jedes Mal, wenn < aufgerufen wird, tauscht das Objekt order , auf das verwiesen wird, die Bedeutung aus. Nun, das ist ziemlich lächerlich, aber wenn du Ordering by const& benutzt hast, musste der Optimierer diese Möglichkeit berücksichtigen und sie bei jedem Aufruf deines Bestellcodes ausschließen!

Sie würden das oben Gesagte nicht tun (und in der Tat ist diese spezielle Implementierung UB, da dies gegen vernünftige Requisiten in std::sort verstoßen würde); aber der Compiler muss beweisen, dass Sie nicht jedesmal "so etwas" gemacht haben (ändern Sie den Code in ordering ), jedes Mal wenn er order folgt oder ihn aufruft! Das bedeutet, den Zustand von order ständig neu zu laden, oder zu inlinen und zu beweisen, dass du nicht so wahnsinnig warst.

Dies zu tun, wenn man einen Wert nimmt, ist um eine Größenordnung schwieriger (und benötigt im Prinzip etwas wie std::ref ). Das Optimierungsprogramm hat ein Funktionsobjekt, es ist lokal und sein Status ist lokal. Alles, was darin gespeichert ist, ist lokal, und der Compiler und der Optimierer wissen, wer genau es legal modifizieren kann.

Jede Funktion, die Sie schreiben, die eine const& einnimmt, die ihren "lokalen Bereich" verlässt (sagen wir eine C-Bibliotheksfunktion), kann nicht davon ausgehen, dass der Zustand von const& gleich geblieben ist. Er muss die Daten von wo auch immer der Zeiger zeigt neu laden.

Nun, ich habe Wert für Wert angegeben, es sei denn, es gibt einen guten Grund. Und es gibt viele gute Gründe; Ihr Typ ist sehr teuer zu bewegen oder zu kopieren, zum Beispiel, ist ein guter Grund. Sie schreiben Daten darauf. Sie möchten, dass es sich ändert, wenn Sie es jedes Mal lesen. Etc.

Das Standardverhalten sollte jedoch pass-by-value sein. Wechseln Sie nur zu Referenzen, wenn Sie einen guten Grund haben, denn die Kosten sind verteilt und schwer feststellbar.

    
Yakk 12.12.2016, 20:29
quelle
0

Ich bin mir nicht sicher, ob ich eine Antwort für dich habe, aber wenn ich meine Objektlebenszeiten richtig habe, denke ich, dass das tragbar ist, sicher und fügt null Overhead oder Komplexität hinzu:

%Vor%     
Richard Hodges 12.12.2016 20:28
quelle