sirve que para funcion c++ swap

c++ - que - swap en c



iter_swap() versus swap(): ¿cuál es la diferencia? (6)

MSDN dice :

swap debe usar con preferencia a iter_swap , que se incluyó en el estándar C ++ para compatibilidad con versiones anteriores.

Pero comp.std.c ++ dice :

La mayoría de los algoritmos STL operan en rangos de iteradores. Por lo tanto, tiene sentido utilizar iter_swap al intercambiar elementos dentro de esos rangos, ya que ese es su propósito: intercambiar los elementos señalados por dos iteradores. Esto permite optimizaciones para secuencias basadas en nodos tales como std::list , mediante el cual los nodos simplemente se vuelven a vincular, en lugar de los datos que realmente se intercambian.

Entonces, ¿cuál es la correcta? ¿Debería usar iter_swap , o debería usar swap ? (¿Es iter_swap solo por compatibilidad con versiones anteriores?) ¿Por qué?


¿Cual deberías usar? Depende de para qué lo estás usando. Porque el intercambio solo funciona para objetos, intercambiando dos enteros independientes o cadenas o dobles. Pero iter_swap funciona bien para matrices y listas, en las que puede intercambiar números en dos listas diferentes como se muestra en cplusplus.com


El estándar en sí tiene muy pocas menciones de iter_swap :

  • Debería tener el efecto de swap(*a, *b) , aunque no hay ninguna estipulación que deba implementarse de esa manera.
  • Los valores sin referencias *a y *b deben ser "intercambiables", lo que implica que swap(*a, *b) debe ser válido, y por lo tanto los tipos sin referencias deben ser idénticos, aunque los tipos de iterador no tienen que serlo.
  • iter_swap es necesario para ser utilizado en la implementación de std::reverse . No se exige tal requisito en ningún otro algoritmo, por lo que parece ser una rareza.

Para tomar prestado lo que se había encontrado de los documentos de SGI :

Estrictamente hablando, iter_swap es redundante. Existe solo por razones técnicas: en algunas circunstancias, algunos compiladores tienen dificultades para realizar la deducción de tipo requerida para interpretar swap(*a, *b) .

Todos estos parecen sugerir que es un artefacto del pasado.


Este parece ser uno de esos escenarios en los que Internet produce una gran cantidad de información conflictiva.

El estándar, en [C++11: 25.3.3/5] , solo dice que iter_swap(a,b) tiene el resultado swap(*a,*b) (y requiere que " a y b sean desreferenciables", y que " *a debe poder intercambiarse con *b "), lo que a primera vista se correlacionaría con la interpretación de MSDN.

Sin embargo, creo que Microsoft ha descuidado considerar la regla de si-si, que debería permitir que una implementación haga que iter_swap más rápido que el swap en ciertos casos (por ejemplo, elementos de una lista vinculada).

Por lo tanto, confiaría en que la cita comp.std.c++ es la más técnicamente precisa de las dos.

Dicho esto, existe un límite bastante estricto sobre la optimización que se puede realizar. Considere, por ejemplo, una implementación de iter_swap sobre elementos de lista enlazados que simplemente vuelven a enlazar nodos en lugar de intercambiar físicamente los valores de los elementos; esta no es una implementación válida, porque se iter_swap el requisito de que el comportamiento observable de iter_swap coincida con el swap .

Por lo tanto, sugeriría que, en la práctica , puede haber poco o ningún beneficio al preferir iter_swap sobre el swap , y yo recomendaría apegarme a este último por simplicidad y coherencia. La semántica del movimiento de C ++ 11 debería hacer que el swap fácil en muchos casos de todos modos.


Has golpeado en la distinción clave.

swap(*a, *b) es una función global que restablece todos los punteros que apuntan a *a para señalar cuál fue el contenido de *b viceversa. Es el viejo tmp = a , a = b , b = tmp swap.

iter_swap es para el caso más limitado de modificar los objetos subyacentes que se iteran para afectar las estructuras de las que formaron parte. Si *a y *b formaban parte de la misma lista vinculada, bastaba con que iter_swap intercambiara sus posiciones en la lista. Esta es una ventaja para cuando desea simplemente ordenar una lista sin invalidar / cambiar los punteros externos a los objetos en la lista. Si tengo un puntero a un objeto de user , no me importa si clasificas la list de objetos de user , no quiero que cambie mi idea de quién es el usuario "actual", por lo tanto, haz una lista más ordenada, no usar el swap .


Leyendo las leyes cuidadosamente:

20.2.2 swap [utility.swap]

  • cambio de espacio de plantilla (T & a, T & b) no exceptuado (is_nothrow_move_constructible :: value && is_nothrow_move_assignable :: value);

    2 Requiere: Tipo T será MoveConstructible y MoveAssignable. (Tabla 20) y (Tabla 22)

    3 Efectos: Intercambia valores almacenados en dos ubicaciones.

  • plantilla void swap (T (& a) [N], T (& b) [N]) noexcept (noexcept (swap (* a, * b)));

    4 Requiere: a [i] será intercambiable con b [i] para todo i en el rango [0, N). (17.6.3.2)

    5 efectos: swap_ranges (a, a + N, b)

25.3.3 intercambio [alg.swap]

  • plantilla void iter_swap (ForwardIterator1 a, ForwardIterator2 b);

    5 Efectos: swap (* a, * b).

    6 Requiere: a y b no serán referenciables. * a será intercambiable con * b. (17.6.3.2)

Por lo tanto, iter_swap es necesario para intercambiar los valores almacenados en dos ubicaciones desreferenciadas o rangos de ubicaciones sin referencias, y cualquier intento de cambiar las referencias o ubicaciones por sí mismos está luchando contra la conformidad. Eso deshabilita claramente la especulación acerca de que las optimizaciones son la razón detrás de std :: iter_swap. En cambio, como señalaba adecuadamente Potatoswatter, la encapsulación y la abstracción son las principales razones de su existencia. std :: iter_swap y std :: swap pertenecen a diferentes capas de abstracción, del mismo modo que std :: swap y cualquier función binaria no miembro llamada "swap" seleccionada mediante resolución de sobrecarga difiere.

Intercambiar funciones de desarrollador y diseñador para comprender el logro del mismo resultado no significa ser el mismo, ya que "incluso declarar que un typedef de un tipo fundamental es solo ruido para un compilador, no es ruido para el lector". Tómalo como una broma, pero podríamos argumentar que C ++ completo es solo un artefacto despreciable que afecta a C, ya que ambos hacen lo mismo mientras están en el nivel más bajo, y así sucesivamente con cualquier bloque de código que represente la abstracción de otro mediante un wrapper. Especialmente cuando la línea es tan delgada como en el caso de std :: iter_swap, "swap" y std :: swap. Tal vez "using std :: swap" tiene solo unos pocos caracteres y desaparece una vez compilado, pero significa inyectar un identificador y crear un mecanismo completo de resolución de sobrecarga. Inyectado una y otra vez, construido una y otra vez, reemplazado una y otra vez, descartado una y otra vez. Lejos de un enfoque abstracto, encapsulado y reciclado.

Exponer el trabajo interno a través de la capa superior proporciona una posibilidad adicional de falla en el mantenimiento. En el dominio de intercambio, falta (o molesta) un "uso de std :: swap" en un diseño de contención de metaprogramación profunda que esperará silenciosamente dentro de la función de plantilla, esperando que un tipo fundamental o c-array trivialmente intercambiable rompa la construcción, si Suerte, o incluso (TM) por medio de recursión infinita. Obviamente, la implementación de un mecanismo extensible debe ser publicada, pero también debe ser respetada. Sobre trivialmente intercambiable, imagine que todo lo que sea moveconstructible y moveassignable es intercambiable contra su propio tipo, incluso si carece de un gancho de resolución swap sobrecargado, y de hecho existen técnicas poco claras para deshabilitar comportamientos intercambiables no deseados.

Teniendo esto en cuenta, quizás todo se pueda reanudar en una interpretación inadecuada del identificador std :: iter_swap en sí mismo: no significa "intercambio de iterador", sino "intercambio iterativo". No se deje engañar por los requisitos estándar sobre los argumentos que son iteradores de ida: en esencia, un puntero es un iterador de acceso aleatorio, por lo tanto, cumple los requisitos. Físicamente pasas por el puntero, lógicamente pasas por el iterador. La comisión generalmente trata de especificar los requisitos mínimos para que una instalación funcione con un comportamiento definido y esperado, nada más. El nombre de "intercambio iterativo" expone correctamente el objetivo y los poderes del mecanismo. el identificador "std :: iter_swap" parece no debido a la confusión generada, pero es demasiado tarde para cambiarlo y deshacer toda la base de código en la que confía.

Siéntase libre de cambiar lo que desee, siempre y cuando funcione, pero por favor, no en mi reloj. Mezclar capas de abstracción no hará llorar al compilador, pero las interfaces son demasiado geniales para evitarlas. En cambio, aquí hay un fragmento para ayudar a guiar en el futuro:

//#include <utility> // std::swap is not required here #include <algorithm> // std::iter_swap is namespace linker { class base { }; class member { }; template<class M = member, class B = base> // requires swappable base and member class link : B { public: void swap(link &other) { // using iterable swapping std::iter_swap(static_cast<B*>(this), static_cast<B*>(&other)); std::iter_swap(&_member, &other._member); } private: M _member; }; template<class base, class member> void swap(link<base,member>& left, link<base,member>& right) { // extending iterable swapping left.swap(right); } } namespace processor { template<class A, class B> void process(A &a, B &b) { // using iterable swapping std::iter_swap(&a, &b); } } int main() { #if !defined(PLEASE_NO_WEIRDNESS) typedef linker::link< linker::link< linker::link< int[1] >, linker::link< void*, linker::link<> > >[2], linker::link< linker::member[3] > > swappable[4]; // just an array of n-ary hierarchies #else typedef linker::link<> swappable; #endif swappable a, b; processor::process(a, b); }

Algunos puntos de interés como orientación adicional:

  • El intercambio significa excepciones lanzadas. La afirmación parece estúpida, pero una vez que lo sabes, la expresión de intercambio no se centra en el rendimiento, sino en la seguridad y solidez extremas.

  • std :: iter_swap muestra una de las muchas características encantadoras pero olvidadas de la metaprogramación: una plantilla no solo resuelve la sobrecarga sino también la resolución del espacio de nombres, permitiendo su uso como el primero en una cadena de espacios de nombres desconocidos y no relacionados. Gracias, una cosa menos de qué preocuparse.

  • Los requisitos intercambiables le permiten usar std :: swap directamente si (y SOLAMENTE SI) puede permitirse suponer que ambos objetivos son fundamentales o c-array a tipos fundamentales, lo que permite al compilador eludir cualquier resolución de sobrecarga. Lamentablemente, esto elimina los parámetros de casi todas las plantillas. El uso de std :: swap implica que ambos objetivos son del mismo tipo (o forzados a ser del mismo tipo).

  • No desperdicie esfuerzos en declarar capacidades intercambiables en un tipo que ya se pueda intercambiar trivialmente consigo mismo, al igual que nuestra clase de plantilla de enlace (intente eliminar linker :: swap, el comportamiento no cambiará en absoluto).
    "Swap" fue diseñado para ser extensible para intercambiar de diferentes tipos,
    automático para los mismos tipos. Mente que un tipo no es "intercambiable" o
    "no intercambiable" por sí mismo, pero "swappable-with" o
    "no intercambiable con" otro tipo.

Finalmente, me pregunto cuántos lectores notarán

  • 20.2.2 swap [utility.swap]

  • 25.3.3 intercambio [alg.swap]

y reconocer que una utilidad no es un algoritmo. En la implementación de Microsoft-Dinkumware, entre otros, std :: iter_swap simplemente vive en el encabezado incorrecto por conveniencia, lo que no está mal. Quizás solo sea su identificador.

Editar: Después de enfrentar algunos errores más relacionados, pensé que podría resumirlos así: un algoritmo es un concepto tan genérico y específico, cada vez que alguien está a punto de especializar a uno de ellos, un diseñador llora en otro lugar. En el caso de std :: iter_swap, dado que el comité claramente no otorga libertad, cualquier intento de modificar el algoritmo como en las especulaciones de reexpedición merecería un significado e identificador diferente. Además, tal vez alguien que perdió contenedores tiene una función de miembro de intercambio, donde se aplican las optimizaciones.

Mejor refactorización para que los objetos de la capa final sean no fundamentales, fundamentales o representen objetos más pesados ​​ocultos (transmitidos si son lo suficientemente pesados). Acepte que la adquisición de recursos debe ser inicialización ( RAII ) y tanto las sobrecargas de nueva supresión como las asignaciones de contenedores tienen un use , para liberar verdaderos beneficios de canje con un esfuerzo adicional cero. Optimice los recursos para que solo mueva datos en la readquisición, luego deje que C ++ diseñe sus tipos de manera fácil, segura y rápida de manera predeterminada.

Lema: en los viejos tiempos, las personas tenían problemas con los datos que estaban demasiado cargados de memoria, demasiado lentos en el disco. Hoy en día, los vectores de iterador se filtran desde grupos de almacenamiento y se transmiten para ser procesados ​​en tuberías paralelas. Mañana los autos conducirán solos. Merece un PostIt.


Sí, ambos hacen lo mismo cuando se usan correctamente . No, std::iter_swap no está en desuso (al colocarse en la sección de características de Compatibilidad §D de la norma). La cita de MSDN es engañosamente desdeñosa. El problema es que no es conveniente usar std::swap correctamente.

Deberías usar iter_swap por la simple razón de que es una abstracción más alta.

swap suele estar sobrecargado para los tipos definidos por el usuario. La forma correcta de llamarlo es

using std::swap; swap( blah, bleh );

no simplemente

std::swap( blah, bleh );

Esto está instalado en §17.6.3.2, en particular ¶3:

El contexto en el que se evalúan swap(t, u) y swap(u, t) garantizará que se seleccione una función binaria no miembro llamada "swap" mediante resolución de sobrecarga (13.3) en un conjunto candidato que incluya:

- las dos plantillas de función de swap definidas en <utility> (20.2) y

- el conjunto de búsqueda producido por búsqueda dependiente de argumento (3.4.2).

iter_swap no es un nombre sobrecargado tan especial, y la personalización de su funcionalidad requiere agregar una especialización de plantilla al namespace std {} de namespace std {} .

Por lo tanto, iter_swap encapsula útilmente la parte de la interfaz Swappable que de otra forma implementaría cada vez.

En realidad, es una interfaz más amigable, independientemente de si alguna vez hay una diferencia semántica para su implementación y sus argumentos particulares. (No se deben pasar por alto las posibles optimizaciones. MSDN puede dar su opinión, pero no pueden anticipar lo que los autores de la biblioteca podrían proporcionar utilizando "interfaces de compatibilidad con versiones anteriores").

En cuanto a una especialización de iter_swap con un resultado observablemente diferente de swap( *a, *b ) , eso parecería ser no conforme con el requisito §25.3.3 / 5,

Efectos: swap(*a, *b) .

El ejemplo que cita suena como una diferencia observable, ya que los punteros a *a y *b son válidos antes y después de la operación. Desafortunadamente, es un error en la implementación de la biblioteca.