È sicuro scambiare due diversi vettori in C ++, usando il metodo std :: vector :: swap?


30

Supponi di avere il seguente codice:

#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;
}

Immagina che il vettore non lo sia std::string, eppure le classi:

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

È ancora sicuro scambiare i due vettori con il std::vector::swapmetodo: WidgetVector.swap(Widget2Vector);o porterà a un UB?

Risposte:


20

È sicuro perché durante l'operazione di scambio non viene creato nulla. std::vectorVengono scambiati solo i membri di dati della classe .

Considera il seguente programma dimostrativo che chiarisce come gli oggetti della classe std::vectorvengono scambiati.

#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;
}

L'output del programma è

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 

Come vedi solo i membri dei dati ptre nvengono scambiati nella funzione di scambio dei membri. Non vengono utilizzate risorse aggiuntive.

Un approccio simile viene utilizzato in classe std::vector.

Come per questo esempio

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

poi ci sono oggetti di diverse classi. Lo scambio di funzioni membro viene applicato a vettori dello stesso tipo.


6
Ma che dire del caso reale del PO , in cui i vettori che vengono scambiati sono di classi diverse ?
Adrian Mole,

4
@AdrianMole Lo scambio di funzioni membro se definito per il tipo dato del vettore. Non è definito per vettori di diversi tipi. Non è una funzione membro modello.
Vlad da Mosca,

"Lo scambio viene applicato a vettori dello stesso tipo" È necessario aggiungere un "solo" tra "is" e "applicato".
SS Anne,

Naturalmente, gli allocatori con stato potrebbero cambiare le cose.
Deduplicatore,

21

Sì, è perfettamente sicuro scambiare vettori dello stesso tipo.

Il vettore sotto il cofano è solo alcuni puntatori che puntano ai dati utilizzati dal vettore e alla "fine" della sequenza. Quando chiami swap, ti basta scambiare quei puntatori tra i vettori. Non devi preoccuparti che i vettori abbiano le stesse dimensioni per questo motivo.

I vettori di tipi diversi non possono essere scambiati con swap. Dovresti implementare la tua funzione che esegue la conversione e lo scambio.


3
Devi guardare più da vicino la seconda parte della domanda.
Mark Ransom,

@MarkRansom Yep. Perso il 2. Aggiornato.
NathanOliver,

13

È sicuro scambiare due diversi vettori in C ++, usando il metodo std :: vector :: swap?

Sì. Lo scambio può essere generalmente considerato sicuro. D'altra parte, la sicurezza è soggettiva e relativa e può essere considerata da diverse prospettive. Pertanto, non è possibile fornire una risposta soddisfacente senza aumentare la domanda in base al contesto e scegliere quale tipo di sicurezza viene preso in considerazione.

È ancora sicuro scambiare i due vettori con il metodo std :: vector :: swap: WidgetVector.swap (Widget2Vector); o porterà ad un UB?

Non ci sarà UB. Sì, è ancora sicuro nel senso che il programma è mal formato.


7

La swapfunzione è definita come segue: void swap( T& a, T& b );. Nota qui che entrambi ae bsono (e devono essere) lo stesso tipo . (Non esiste una tale funzione definita con questa firma:, void swap( T1& a, T2& b )poiché non avrebbe senso!)

Allo stesso modo, la swap()funzione membro della std::vectorclasse è definita come segue:

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

Ora, poiché non esiste una definizione "equivalente" con una sostituzione del modello (consultare Specializzazioni esplicite dei modelli di funzione ) per il parametro della funzione (che sarebbe del formato:) template <typename T2> void swap(std::vector<T2>& other), quel parametro deve essere un vettore dello stesso tipo (modello) di la classe 'chiamante' (cioè deve anche essere una vector<T1>).

Tu std::vector<Widget>e std::vector<Widget2>sei due tipi diversi , quindi la chiamata a swapnon verrà compilata, sia che tu provi a usare la funzione membro di uno degli oggetti (come fa il tuo codice), sia usando la specializzazione della std::swap()funzione che accetta due std:vectoroggetti come parametri.


8
std::vector::swapè una funzione membro, come può essere una specializzazione di una funzione modello indipendente ???
Aconcagua,

1
Risultato giusto, ragionamento sbagliato.
NathanOliver,

2
@AdrianMole Anche se esiste una specializzazione per std::swap, non è quello che utilizza l'OP. Quando lo fai First.swap(Second);, chiami std::vector::swapquale è una funzione diversa dastd::swap
NathanOliver il

1
Scusa, mio ​​cattivo ... Il tuo collegamento va direttamente alla documentazione della specializzazione std :: swap per i vettori. Ma questo è diverso dalla funzione membro , che esiste anche ed è usata in questione (solo).
Aconcagua,

2
Il ragionamento in realtà è analogo: il membro è definito come void swap(std::vector& other)(cioè std::vector<T>), non come template <typename U> void swap(std::vector<U>& other)(supponendo che T sia un parametro di tipo per il vettore stesso).
Aconcagua,

4

Non è possibile scambiare vettori di due tipi diversi, ma si tratta di un errore di compilazione anziché di UB. vector::swapaccetta solo vettori dello stesso tipo e allocatore.

Non sono sicuro che funzionerà, ma se vuoi che un vettore contenente Widget2s sia convertito da Widgets puoi provare questo:

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

Widget2dovrà essere mossa costruibile da Widget.


0

using std::swap; swap(a, b);e a.swap(b);hanno la stessa semantica esatta in cui funziona quest'ultima; almeno per qualsiasi tipo sano di mente. Tutti i tipi standard sono sani al riguardo.

A meno che tu non usi un allocatore interessante (che significa stateful, non sempre uguale, e non propagato su container swap, vedi std::allocator_traits), scambiare due std::vectors con gli stessi argomenti template è solo uno scambio noioso di tre valori (per capacità, dimensioni e dati- puntatore). E lo scambio di tipi di base, assenza di corse di dati, è sicuro e non può essere lanciato.

Questo è persino garantito dallo standard. Vedere std::vector::swap().

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.