¿Es seguro intercambiar dos vectores diferentes en C ++, utilizando el método std :: vector :: swap?


30

Supongamos que tiene el siguiente código:

#include <iostream>
#include <string>
#include <vector>

int main()
{
    std::vector<std::string> First{"example", "second" , "C++" , "Hello world" };
    std::vector<std::string> Second{"Hello"};

    First.swap(Second);

    for(auto a : Second) std::cout << a << "\n";
    return 0;
}

Imagina que los vectores no lo son std::string, sin embargo, las clases:

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

¿Todavía es seguro intercambiar los dos vectores con el std::vector::swapmétodo: WidgetVector.swap(Widget2Vector);o dará lugar a un UB?

Respuestas:


20

Es seguro porque no se crea nada durante la operación de intercambio. Solo std::vectorse intercambian los miembros de datos de la clase .

Considere el siguiente programa demostrativo que deja en claro cómo std::vectorse intercambian los objetos de la clase .

#include <iostream>
#include <utility>
#include <iterator>
#include <algorithm>
#include <numeric>

class A
{
public:
    explicit A( size_t n ) : ptr( new int[n]() ), n( n )
    {
        std::iota( ptr, ptr + n, 0 );   
    }

    ~A() 
    { 
        delete []ptr; 
    }

    void swap( A & a ) noexcept
    {
        std::swap( ptr, a.ptr );
        std::swap( n, a.n );
    }

    friend std::ostream & operator <<( std::ostream &os, const A &a )
    {
        std::copy( a.ptr, a.ptr + a.n, std::ostream_iterator<int>( os, " " ) );
        return os;
    }

private:    
    int *ptr;
    size_t n;
};

int main() 
{
    A a1( 10 );
    A a2( 5 );

    std::cout << a1 << '\n';
    std::cout << a2 << '\n';

    std::cout << '\n';

    a1.swap( a2 );

    std::cout << a1 << '\n';
    std::cout << a2 << '\n';

    std::cout << '\n';

    return 0;
}

La salida del programa es

0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 

0 1 2 3 4 
0 1 2 3 4 5 6 7 8 9 

Como ve solo los miembros de datos ptry nse intercambian en el intercambio de funciones miembro. No se utilizan recursos adicionales.

Un enfoque similar se utiliza en la clase std::vector.

En cuanto a este ejemplo

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

entonces hay objetos de diferentes clases. El intercambio de funciones miembro se aplica a vectores del mismo tipo.


66
Pero, ¿qué pasa con el caso real del OP , donde los vectores que se intercambian son de diferentes clases?
— Adrian Mole

44
@AdrianMole El intercambio de funciones miembro si está definido para el tipo dado del vector. No está definido para vectores de diferentes tipos. No es una función miembro de plantilla.
— Vlad desde Moscú el

"el intercambio se aplica a los vectores del mismo tipo" Debe agregar un "solo" entre "es" y "aplicado".
— SS Anne

Por supuesto, los asignadores con estado pueden cambiar las cosas.
— Deduplicador

21

Sí, esto es perfectamente seguro para intercambiar vectores del mismo tipo.

El vector debajo del capó son solo algunos indicadores que apuntan a los datos que usa el vector y al "final" de la secuencia. Cuando llamas a swap solo intercambias esos punteros entre los vectores. No necesita preocuparse de que los vectores sean del mismo tamaño debido a esto.

Los vectores de diferentes tipos no se pueden intercambiar usando swap. Tendría que implementar su propia función que realiza la conversión y el intercambio.


3
Debes mirar más de cerca la segunda parte de la pregunta.
— Mark Ransom

@MarkRansom Sí. Perdí el 2. Actualizado.
— NathanOliver

13

¿Es seguro intercambiar dos vectores diferentes en C ++, utilizando el método std :: vector :: swap?

Si. El intercambio generalmente se puede considerar seguro. Por otro lado, la seguridad es subjetiva y relativa y puede considerarse desde diferentes perspectivas. Como tal, no es posible dar una respuesta satisfactoria sin aumentar la pregunta con un contexto y elegir qué tipo de seguridad se está considerando.

¿Sigue siendo seguro intercambiar los dos vectores con el método std :: vector :: swap: WidgetVector.swap (Widget2Vector); o conducirá a una UB?

No habrá UB. Sí, todavía es seguro en el sentido de que el programa está mal formado.


7

La swapfunción se define como sigue: void swap( T& a, T& b );. Tenga en cuenta aquí que ambos ay bson (y tienen que ser) del mismo tipo . (No existe tal función definida con esta firma: ¡ void swap( T1& a, T2& b )ya que no tendría sentido!)

Del mismo modo, la swap()función miembro de la std::vectorclase se define de la siguiente manera:

template<class T1> class vector // Note: simplified from the ACTUAL STL definition
{
//...
public:
    void swap( vector& other );
//...
};

Ahora, como no existe una definición 'equivalente' con una anulación de plantilla (consulte Especializaciones explícitas de plantillas de función ) para el parámetro de función (que sería de la forma:) template <typename T2> void swap(std::vector<T2>& other), ese parámetro debe ser un vector del mismo tipo (plantilla) que la clase 'llamante' (es decir, también debe ser a vector<T1>).

Tu std::vector<Widget>y std::vector<Widget2>son dos tipos diferentes , por lo que la llamada a swapno se compilará, ya sea que intentes usar la función miembro de cualquiera de los objetos (como lo hace tu código) o usar la especialización de la std::swap()función que toma dos std:vectorobjetos como parámetros.


8
std::vector::swapes una función miembro, ¿cómo puede ser eso una especialización de una función de plantilla independiente?
— Aconcagua

1
Resultado correcto, razonamiento incorrecto.
— NathanOliver

2
@AdrianMole Si bien existe una especialización para std::swap, eso no es lo que está utilizando el OP. Cuando lo haces First.swap(Second);, llamas, std::vector::swapque es una función diferente destd::swap
— NathanOliver

1
Lo siento, mi mal ... Su enlace va directamente a la documentación de la especialización std :: swap para vectores. Pero eso es diferente de la función miembro , que también existe y se usa en cuestión (solo).
— Aconcagua

2
El razonamiento en realidad es análogo: el miembro se define como void swap(std::vector& other)(es decir std::vector<T>), no como template <typename U> void swap(std::vector<U>& other)(suponiendo que T sea un parámetro de tipo para el vector mismo).
— Aconcagua

4

No puede intercambiar vectores de dos tipos diferentes, pero es un error de compilación en lugar de UB. vector::swapsolo acepta vectores del mismo tipo y asignador.

No estoy seguro de si esto funcionará, pero si desea un vector que contenga Widget2s convertido de Widgets, puede intentar esto:

std::vector<Widget2> Widget2Vector(
    std::make_move_iterator(WidgetVector.begin()),
    std::make_move_iterator(WidgetVector.end())
);

Widget2tendrá que ser movible desde Widget.


0

using std::swap; swap(a, b);y a.swap(b);tienen exactamente la misma semántica donde trabaja este último; al menos para cualquier tipo sano. Todos los tipos estándar son sanos en ese sentido.

A menos que use un asignador interesante (es decir, con estado, no siempre igual y no propagado en el intercambio de contenedores, ver std::allocator_traits), intercambiar dos std::vectors con los mismos argumentos de plantilla es solo un intercambio aburrido de tres valores (para capacidad, tamaño y datos) puntero). Y el intercambio de tipos básicos, carreras de datos ausentes, es seguro y no se puede lanzar.

Eso incluso está garantizado por el estándar. Ver std::vector::swap().

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.