¿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?

Emanuele Oggiano
fuente

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.

Vlad de Moscú
fuente
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.

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

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

Adrian Mole
fuente
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.

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

Deduplicador
fuente