2011-11-16 4 views
18

C++ 11 §27.5.4.2/21:¿Por qué `basic_ios :: swap` solo realiza un intercambio parcial?

void swap(basic_ios& rhs);

Efectos: Los estados de *this y rhs serán intercambiados, excepto que rdbuf() devolverá el mismo valor que devuelto antes de la llamada de función, y rhs.rdbuf() devolverá el mismo valor que devolvió antes de la llamada de función.

¿Para qué sirve este intercambio parcial?

¿Puede causar problemas?

+0

Alf P. Steinbach haciendo una pregunta. Increíble. : | – Nawaz

+0

Esto es realmente sorprendente. Si cambio dos cosas, realmente esperaría que cambien; si tuviera un error y descubriera que no estaba intercambiando 'rdbuf', asumiría que era un error de implementación. – GManNickG

+0

Muy sorprendente de hecho. Revisé el primer FinalDraft que tuve (n3092) y es exactamente lo mismo. Me pregunto si fue idéntico en C++ 03, ¿quizás un remanente heredado? –

Respuesta

20

Puede culparme por esto. El comité ha tratado de cambiar (dos veces creo), pero cada vez la solución terminó rompiendo cosas.

La semántica de intercambio y movimiento se ha adaptado a nuestro sistema de E/S una década después de su diseño. Y no fue un ajuste perfectamente limpio.

Tenga en cuenta que basic_ios::swap es protegida función de miembro y no hay variante de ámbito de espacio de nombres. Por lo tanto, esto solo puede invocarse desde una clase derivada (típicamente istream/ostream). Tenga en cuenta que i/o_stream::swap también está protegido y sin variante de ámbito de espacio de nombres. Y su especificación es llamar a la clase base swap y luego intercambiar cualquier información local (como gcount en istream).

Finalmente, en el nivel string/filestream obtienes lo que considerarías una "normal" swap: miembro público y variantes de espacio de nombres de espacio. En este nivel, tiene un miembro de datos string/file buffer (el rdbuf) y la clase base. El swap en este nivel simplemente intercambia la base y los miembros de datos.

La característica que complica de todo esto es que el rdbuf() en la clase base es en realidad un puntero referencia a sí misma a la streambuf (basic_filebuf o basic_stringbuf) y de clase derivada que Es por eso que no quiere que la clase base para intercambiar estos punteros de referencia automática.

Esto hace que la base swap sea extraña, pero todos están protegidos, salvo los clientes derivados. Y el código para swap del cliente derivado es subsecuentemente aparentemente simple. Y en el nivel derivado, swap se hace público y se comporta de la manera en que los clientes públicos lo esperan.

Un baile similar está hecho para la construcción de movimiento y la asignación de movimiento. La construcción de movimientos se complica aún más por el hecho de que la clase base es una base virtual, y por lo tanto su constructor no es llamado por la clase derivada más directamente.

Fue divertido. Se ve raro. Pero finalmente funciona. ;-)

Corrección Leve:

Alberto Ganesh Barbati is responsible para proteger swap a nivel i/ostream. Fue una muy buena llamada de su parte que me había perdido por completo con mi primer diseño.

+0

Entonces, tl; dr: ¿La clase base no sabe cómo intercambiar el buffer de la clase derivada y es por eso que deja eso a las clases derivadas? ¿Qué tal una función de intercambio virtual en 'basic_streambuf'? – Xeo

+0

No he creado un prototipo de ese diseño, pero supongo que podría funcionar. Sin embargo, la especificación en el nivel derivado parece un poco extraña: cambie su clase base pero no su miembro de datos. –

+0

Es cierto que parece que la especificación básica o derivada tiene que parecer extraña con la implementación actual de iostreams. En una nota lateral, ¿alguna vez pensaste en rediseñar iostreams o reemplazarlos por algo completamente diferente? Puedo ver que esto perjudicaría negativamente la compatibilidad con versiones anteriores, pero suponiendo que eso no suponga ningún problema. ¿Existen ideas que circulan a través del comité, p. para la inclusión en Boost o sth? – Xeo

2

sólo tengo una respuesta especulativa ...

Si el autor supone que una corriente puede usar un buffer interno (por ejemplo, un miembro de datos char buffer[50]), a continuación, esta disposición es necesaria como, obviamente, el contenido de las memorias intermedias pueden intercambiarse, pero su dirección permanecerá sin cambios.

No sé si está realmente permitido o no.

+0

puede reemplazar el búfer mediante la función 'rdbuf'. –

+0

@ AlfP.Steinbach: Sí, pero ¿de dónde viene el buffer original? Está destinado a ser asignado en alguna parte, y no encuentro nada que me impida usar una matriz interna para ello. –

+1

el búfer es un objeto de tipo 'basic_streambuf ', o derivado. se puede reemplazar a través de 'stream.rdbuf (pMyNewBuffer)'. el destructor 'basic_ios' está documentado como que no destruye el objeto' rdbuf() ', por lo que es completamente posible poner ese objeto como un miembro directo, pero la clase todavía tiene que soportar cambiar el puntero' rdbuf() '. –

Cuestiones relacionadas