Pregunta ¿Por qué std :: queue :: pop devuelve valor?


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   clientes a usar front () para inspeccionar el valor en el frente de la cola "

Pero inspeccionar un elemento desde el frente () también requería que ese elemento se copiara 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á temporalmente en RHS que se asignará al resultado, y en el caso  si devuelve por referencia, el resultado se invalidará después de la operación pop * /

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

en quinta línea cout object 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.


75
2017-07-30 11:29


origen


Respuestas:


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 un pop que devolvió el elemento reventado no es seguro en presencia de excepciones (tiene que devolver por valor y crear así 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 copias de T arroja 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 intentos y propósitos (no importa cómo capte 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 reventado (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())


71
2017-07-30 11:42



La página a la que se ha vinculado responde 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é uno 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 devolver por valor en lugar de por referencia: el retorno por referencia crearía un puntero colgante. Sin embargo, el retorno 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 exija que los clientes utilicen 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 debe escribir.


22
2017-07-30 11:39



pop no puede devolver una referencia al valor que se elimina, ya que se elimina de la estructura de datos, entonces, ¿a qué se refiere la referencia? Podría regresar por valor, pero ¿y si el resultado del pop no está almacenado en ninguna parte? Entonces se desperdicia tiempo copiando el valor innecesariamente.


3
2017-07-30 11:37



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

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

Si pop devuelve una referencia, como esta:

value_type& pop();

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

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

Por otro lado, si devuelve un valor directamente:

value_type pop();

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

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

2
2017-07-30 11:38



Puedes hacer esto totalmente:

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

O bien, 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 fraseología "más sensible" es una forma subjetiva de "examinamos patrones de uso y encontramos más necesidad de una división". (Tenga la seguridad: el lenguaje C ++ no evoluciona a la ligera ...)


0
2017-07-30 11:37



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 reventado.

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


0
2018-06-28 19:27



A partir de Cx11 sería posible archivar el comportamiento deseado usando la semántica de movimiento. Como pop_and_move. Por lo tanto, no se llamará al constructor de copias, y el rendimiento dependerá únicamente del constructor de movimientos.


0
2018-01-24 01:56