c++ - ¿Está bien "mover" un objeto de una cola, si está a punto de saltar de él?
c++11 boost (4)
He estado trabajando en un analizador de commands
(que son envoltorios sofisticados alrededor de grandes conjuntos de datos), y tengo una cola en la que residen los comandos no manejados. Si necesito un comando, lo pregunto con un código como este:
boost::optional<command> get_command() {
if (!has_command()) return boost::optional<command>(nullptr);
else {
boost::optional<command> comm(command_feed.front()); //command_feed is declared as a std::queue<command>
command_feed.pop();
return comm;
}
}
El problema es que estos comandos pueden tener un tamaño de megabytes, en las circunstancias adecuadas, y deben analizarse con bastante rapidez. Mi pensamiento fue que podría optimizar la transferencia a un movimiento así:
boost::optional<command> get_command() {
if (!has_command()) return boost::optional<command>(nullptr);
else {
boost::optional<command> comm(std::move(command_feed.front())); //command_feed is declared as a std::queue<command>
command_feed.pop();
return comm;
}
}
Y parece funcionar para este caso específico, pero ¿puede usarse como una solución de propósito general para cualquier objeto RAII mantenido correctamente, o debería estar haciendo otra cosa?
Depende de lo que haga el constructor de movimientos para tu tipo. Si deja el objeto original en un estado que puede ser destruido de manera segura, entonces todo está bien. Si no, entonces puedes estar en problemas. Tenga en cuenta que los comentarios sobre condiciones previas y estados válidos son sobre restricciones en los tipos definidos en la biblioteca estándar . Los tipos que define no tienen esas restricciones, excepto en la medida en que usan tipos de la biblioteca estándar. Así que mire a su constructor de movimientos para ordenar lo que puede y no puede hacer con un objeto movido desde.
Sí, esto es perfectamente seguro:
std::queue<T> q;
// add stuff...
T top = std::move(q.front());
q.pop();
pop()
no tiene condiciones previas en el primer elemento en el q
tiene un estado específico, y como no está usando q.front()
posteriormente, no tiene que lidiar con que el objeto ya no sea q.front()
.
Suena como una buena idea para hacer!
mover un objeto puede dejarlo en un estado no válido. Sus invariantes ya no están garantizadas. Usted estaría seguro de sacarlo de una cola no intrusiva.
El std :: move en sí mismo no hace más que decirle al compilador que puede seleccionar una rutina de comunicación que toma un valor r.
Una rutina de comunicaciones bien escrita, entonces robaría la representación del objeto antiguo para el nuevo objeto. Por ejemplo, simplemente copie los punteros al nuevo objeto y ponga a cero los punteros en el antiguo objeto (de esa manera el antiguo destructor de objetos no destruirá las matrices).
Si comm no está sobrecargado para hacer esto, no habrá ningún beneficio para std :: mov.
Si Siempre que el argumento de la plantilla contenedora de su std::queue
asegure de que no haya condiciones previas en el estado de sus valores contenidos para pop_front()
; el valor predeterminado para std::queue
es std::deque
y eso ofrece la garantía.
Mientras se asegure lo que escribí en el párrafo anterior, estará completamente seguro. Estás a punto de eliminar ese elemento de tu cola, por lo que no hay razón para no moverlo, ya que estás tomando posesión de ese objeto.