Frage Warum gibt std :: queue :: pop keinen Rückgabewert zurück?


Ich ging das durch Seite aber ich bin nicht in der Lage, den Grund dafür zu bekommen. Da wird es erwähnt

"Es ist sinnvoller, überhaupt keinen Wert zurückzugeben und zu verlangen   Clients, Front () zu verwenden, um den Wert an dem Anfang der Warteschlange zu überprüfen "

Bei der Überprüfung eines Elements von front () musste dieses Element jedoch in lvalue kopiert werden. Zum Beispiel in diesem Codesegment

std::queue<int> myqueue;
int myint;
int result;
std::cin >> myint;
myqueue.push (myint);

/ * hier wird temporär auf RHS erstellt, das dem Ergebnis zugewiesen wird, und für den Fall  Wenn durch Verweis zurückgegeben wird, wird das Ergebnis nach dem Popup-Vorgang ungültig gemacht.

result = myqueue.front();  //result.
std::cout << ' ' << result;
myqueue.pop();

in der fünften Zeile cout Objekt erstellt zuerst eine Kopie von myqueue.front () und weist dann das Ergebnis zu. Also, was ist der Unterschied, Pop-Funktion könnte das gleiche getan haben.


75
2017-07-30 11:29


Ursprung


Antworten:


Also, was ist der Unterschied, Pop-Funktion könnte das gleiche getan haben.

Es könnte tatsächlich das Gleiche getan haben. Der Grund dafür ist, dass ein Pop, der das Popup-Element zurückgibt, in der Gegenwart von Ausnahmen unsicher ist (muss nach Wert zurückgegeben werden und somit eine Kopie erstellen).

Betrachten Sie dieses Szenario (mit einer naiven / erfundenen Pop-Implementierung, um meinen Punkt zu verdeutlichen):

template<class T>
class queue {
    T* elements;
    std::size_t top_position;
    // stuff here
    T pop()
    {
        auto x = elements[top_position];
        // TODO: call destructor for elements[top_position] here
        --top_position;  // alter queue state here
        return x;        // calls T(const T&) which may throw
    }

Wenn der Kopierkonstruktor von T bei Rückgabe zurückkehrt, haben Sie den Status der Warteschlange bereits geändert (top_position in meiner naiven Implementierung) und das Element wird aus der Warteschlange entfernt (und nicht zurückgegeben). Für alle Absichten und Zwecke (egal wie Sie die Ausnahme in Client-Code abfangen) ist das Element an der Spitze der Warteschlange verloren.

Diese Implementierung ist auch ineffizient, wenn Sie den herausgegriffenen Wert nicht benötigen (d. H. Er erstellt eine Kopie des Elements, das niemand verwenden wird).

Dies kann sicher und effizient mit zwei getrennten Operationen (void pop und const T& front()).


71
2017-07-30 11:42



Die Seite, die Sie verlinkt haben, beantwortet Ihre Frage.

Um den ganzen relevanten Abschnitt zu zitieren:

Man könnte sich fragen, warum pop () void anstatt value_type zurückgibt. Das heißt, warum muss man front () und pop () verwenden, um das Element am Anfang der Warteschlange zu untersuchen und zu entfernen, anstatt die beiden in einer einzigen Elementfunktion zu kombinieren? In der Tat gibt es einen guten Grund für dieses Design. Wenn pop () das Front-Element zurückgeben würde, müsste es nach Wert und nicht nach Verweis zurückgeben: Die Rückgabe als Referenz würde einen ungeeigneten Zeiger erzeugen. Die Rückgabe nach Wert ist jedoch ineffizient: Es handelt sich um mindestens einen redundanten Kopierkonstruktoraufruf. Da es unmöglich ist, dass pop () einen Wert so zurückgibt, dass es sowohl effizient als auch korrekt ist, ist es sinnvoller, überhaupt keinen Wert zurückzugeben und Clients zu veranlassen, front () zu verwenden, um den Wert zu prüfen die Vorderseite der Warteschlange.

C ++ wurde mit Blick auf die Anzahl der Codezeilen entwickelt, die der Programmierer schreiben muss.


22
2017-07-30 11:39



pop kann keine Referenz auf den Wert zurückgeben, der entfernt wurde, da er aus der Datenstruktur entfernt wird. Worauf sollte sich die Referenz beziehen? Es könnte nach Wert zurückgegeben werden, aber was passiert, wenn das Pop-Ergebnis nicht irgendwo gespeichert wird? Dann wird Zeit verschwendet, den Wert unnötigerweise zu kopieren.


3
2017-07-30 11:37



Bei der aktuellen Implementierung gilt dies:

int &result = myqueue.front();
std::cout << result;
myqueue.pop();

Wenn Pop eine Referenz wie folgt zurückgeben würde:

value_type& pop();

Dann könnte der folgende Code abstürzen, da die Referenz nicht mehr gültig ist:

int &result = myqueue.pop();
std::cout << result;

Auf der anderen Seite, wenn es direkt einen Wert zurückgeben würde:

value_type pop();

Dann müssten Sie eine Kopie erstellen, damit dieser Code funktioniert, was weniger effizient ist:

int result = myqueue.pop();
std::cout << result;

2
2017-07-30 11:38



Sie können dies vollständig tun:

std::cout << ' ' << myqueue.front();

Wenn Sie den Wert in einer Variablen haben möchten, verwenden Sie eine Referenz:

const auto &result = myqueue.front();
if (result > whatever) do_whatever();
std::cout << ' ' << result;

Außerdem: Die Formulierung "vernünftiger" ist eine subjektive Form von "wir haben Nutzungsmuster untersucht und mehr Bedarf für eine Spaltung gefunden". (Seien Sie versichert: Die C ++ - Sprache entwickelt sich nicht leichtfertig ...)


0
2017-07-30 11:37



Ich denke, die beste Lösung wäre, etwas hinzuzufügen wie

std::queue::pop_and_store(value_type& value);

wo value den popping-Wert erhalten wird.

Der Vorteil besteht darin, dass es mit einem Verschiebungszuweisungsoperator implementiert werden kann, während mit Front + Pop eine Kopie erstellt wird.


0
2018-06-28 19:27



Ausgehend von Cx11 wäre es möglich, das gewünschte Verhalten mit der Bewegungssemantik zu archivieren. Wie pop_and_move. Der Kopierkonstruktor wird also nicht aufgerufen, und die Leistung hängt nur vom Verschiebungskonstruktor ab.


0
2018-01-24 01:56