
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.

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()
).

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

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;

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.

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…)

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.
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 utilizastd::queue<T, std::list<T>>
entonces no hay preocupación por la referencia proporcionada porfront()
siendo invalidado por unpush()
. pero debes saber su patrón de uso y debe documentar sus limitaciones.– jwm
14/09/2018 a las 20:23