¿Por qué std::queue::pop no devuelve el valor?

8 minutos de lectura

¿Por que stdqueuepop no devuelve el valor
carpeta

pasé por esto página pero no puedo obtener la razón de lo mismo. Allí se menciona que

“es más sensato que no devuelva ningún valor y que requiera que los clientes usen front() para inspeccionar el valor al principio de la cola”

Pero inspeccionar un elemento desde front() también requería que ese elemento fuera copiado en lvalue. Por ejemplo, en este segmento de código

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

/* aquí se creará un temporal en RHS que se asignará al resultado, y en caso de que se devuelva por referencia, el resultado no será válido después de la operación pop */

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

en quinta línea cout El objeto primero crea una copia de myqueue.front() y luego la asigna al resultado. Entonces, cuál es la diferencia, la función pop podría haber hecho lo mismo.

  • Debido a que se implementa de esta manera (es decir, void std::queue::pop();).

    – 101010

    30 de julio de 2014 a las 11:37

  • La pregunta ha sido respondida, pero como nota al margen: si realmente desea un pop que regrese, puede implementarse fácilmente con una función gratuita: ideone.com/lnUzf6

    – erorika

    30 de julio de 2014 a las 11:52

  • Su enlace es a la documentación de STL. Pero está preguntando sobre la biblioteca estándar de C ++. Cosas diferentes.

    – juanchopanza

    30 de julio de 2014 a las 11:55

  • “Pero inspeccionar un elemento de front() también requería que ese elemento se copiara en lvalue” – no, no lo hace. front devuelve una referencia, no un valor. Puede inspeccionar el valor al que se refiere sin copiarlo.

    –Mike Seymour

    30 de julio de 2014 a las 12:10


  • @KeminZhou, el modelo que describe requiere una copia. Quizás. Si desea multiplexar el consumo de la cola, entonces sí, debe hacer una copia antes de liberar el bloqueo de la cola. Sin embargo, si solo le importa separar la entrada y la salida, entonces no necesitar una cerradura para inspeccionar el frente. Puede esperar para bloquear hasta que termine de consumirlo y necesite llamar pop(). Si utiliza std::queue<T, std::list<T>> entonces no hay preocupación por la referencia proporcionada por front() siendo invalidado por un push(). pero debes saber su patrón de uso y debe documentar sus limitaciones.

    – jwm

    14/09/2018 a las 20:23


1646756710 634 ¿Por que stdqueuepop no devuelve el valor
utnapistim

Entonces, cuál es la diferencia, la función pop podría haber hecho lo mismo.

De hecho, podría haber hecho lo mismo. La razón por la que no lo hizo es porque una ventana emergente que devolvió el elemento emergente no es segura en presencia de excepciones (tener que devolver por valor y, por lo tanto, crear una copia).

Considere este escenario (con una implementación pop ingenua/inventada, para ilustrar mi punto):

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
    }

Si el constructor de copia de T lanza al regresar, ya ha alterado el estado de la cola (top_position en mi implementación ingenua) y el elemento se elimina de la cola (y no se devuelve). Para todos los efectos (no importa cómo detecte la excepción en el código del cliente), el elemento en la parte superior de la cola se pierde.

Esta implementación también es ineficiente en el caso de que no necesite el valor extraído (es decir, crea una copia del elemento que nadie usará).

Esto se puede implementar de manera segura y eficiente, con dos operaciones separadas (void pop y const T& front()).

  • mmm… eso tiene sentido

    – cbinder

    30 de julio de 2014 a las 12:01

  • Nota de C ++ 11: si T tiene un constructor de movimiento noexcept barato (que suele ser el caso para el tipo de objetos que se colocan en pilas), entonces devolver por valor es eficiente y seguro contra excepciones.

    – Romano L

    12 de diciembre de 2014 a las 22:08


  • @DavidRodríguez-dribeas: No estoy sugiriendo ningún cambio, mi punto era que algunos de los inconvenientes mencionados se vuelven menos problemáticos con C++11.

    – Romano L

    13 de junio de 2015 a las 15:36

  • pero porque se llama pop? eso es bastante contrario a la intuición. podría ser nombrado dropy luego queda claro para todos que no hace estallar el elemento, sino que lo suelta en su lugar…

    – UeliDeSchwert

    18 de diciembre de 2017 a las 8:59

  • @utnapistim: la operación “pop” de facto siempre ha sido tomar el elemento superior de una pila y devolverlo. Cuando me encontré por primera vez con las pilas STL, me sorprendió, por decir lo menos, sobre pop sin devolver nada. Ver por ejemplo en wikipedia.

    – Cris Luengo

    20/01/2018 a las 19:50

¿Por que stdqueuepop no devuelve el valor
BPF2010

La página a la que se ha vinculado responde a su pregunta.

Para citar toda la sección relevante:

Uno podría preguntarse por qué pop() devuelve void, en lugar de value_type. Es decir, ¿por qué se debe usar front() y pop() para examinar y eliminar el elemento al principio de la cola, en lugar de combinar los dos en una sola función miembro? De hecho, hay una buena razón para este diseño. Si pop() devolviera el elemento frontal, tendría que devolverlo por valor en lugar de por referencia: devolver por referencia crearía un puntero colgante. Sin embargo, la devolución por valor es ineficiente: implica al menos una llamada de constructor de copia redundante. Dado que es imposible que pop() devuelva un valor de manera que sea eficiente y correcto, es más sensato que no devuelva ningún valor y que requiera que los clientes usen front() para inspeccionar el valor en el frente de la cola.

C ++ está diseñado teniendo en cuenta la eficiencia, sobre la cantidad de líneas de código que el programador tiene que escribir.

  • Quizás, pero la verdadera razón es que es imposible implementar una versión segura de excepción (con la garantía fuerte) para una versión de pop que devuelve un valor.

    – James Kanze

    30 de julio de 2014 a las 12:35


pop no puede devolver una referencia al valor que se elimina, ya que se elimina de la estructura de datos, entonces, ¿a qué debería referirse la referencia? Podría devolver por valor, pero ¿y si el resultado de pop no se almacena en ningún lado? Entonces se pierde tiempo copiando el valor innecesariamente.

  • La verdadera razón es la seguridad excepcional. No hay forma segura de tener una “transacción” con la pila (o el elemento permanece en la pila o se le devuelve) si pop devoluciones y el acto de devolución puede resultar en una excepción. Obviamente, tendría que eliminar el elemento antes de devolverlo, y luego, si algo arroja, el elemento podría perderse irrevocablemente.

    – Jon

    30 de julio de 2014 a las 11:45


  • ¿Esta preocupación todavía se aplica con un constructor de movimiento fi a noexcept? (Por supuesto, 0 copias son más eficientes que dos movimientos, pero eso podría abrir la puerta a una excepcional combinación frontal+pop segura y eficiente)

    – pimienta

    30 de julio de 2014 a las 11:45


  • @peppe, podría devolver por valor si conoce el value_type tiene un constructor de movimiento nothrow, pero la interfaz de la cola sería diferente según el tipo de objeto que almacene en él, lo que no sería útil.

    –Jonathan Wakely

    30 de julio de 2014 a las 11:47


  • @peppe: Eso sería seguro, pero stack es una clase de biblioteca genérica y, por lo tanto, cuanto más tiene que asumir sobre el tipo de elemento, menos útil es.

    – Jon

    30 de julio de 2014 a las 11:48

  • Sé que STL no permitiría eso, pero eso significa que uno puede crear su propia clase de cola con blackjack y ^W^W^W con esta función 🙂

    – pimienta

    30 de julio de 2014 a las 11:52

1646756710 250 ¿Por que stdqueuepop no devuelve el valor
Jan Ruegg

Con la implementación actual, esto es válido:

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

Si pop devolviera una referencia, así:

value_type& pop();

Entonces el siguiente código podría fallar, ya que la referencia ya no es válida:

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

Por otro lado, si devolvería un valor directamente:

value_type pop();

Entonces necesitaría hacer una copia para que funcione este código, que es menos eficiente:

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

¿Por que stdqueuepop no devuelve el valor
Viacheslav Putsenko

A partir de C ++ 11, sería posible archivar el comportamiento deseado utilizando la semántica de movimiento. Me gusta pop_and_move. Por lo tanto, no se llamará al constructor de copia, y el rendimiento dependerá solo del constructor de movimiento.

  • No, la semántica de movimiento no crea mágicamente pop excepción segura.

    – LF

    25 de mayo de 2019 a las 1:43

1646756712 54 ¿Por que stdqueuepop no devuelve el valor
xtofl

Totalmente puedes hacer esto:

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

O, si desea el valor en una variable, use una referencia:

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

Además de eso: la expresión ‘más sensato’ es una forma subjetiva de ‘miramos los patrones de uso y encontramos más necesidad de una división’. (Tenga la seguridad: el lenguaje C++ no está evolucionando a la ligera…)

  • No, la semántica de movimiento no crea mágicamente pop excepción segura.

    – LF

    25 de mayo de 2019 a las 1:43

1646756713 407 ¿Por que stdqueuepop no devuelve el valor
Masse Nicolás

Creo que la mejor solución sería agregar algo como

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

donde el valor recibirá el valor emergente.

La ventaja es que podría implementarse usando un operador de asignación de movimiento, mientras que usar front + pop hará una copia.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad