Ist es sicher, zwei verschiedene Vektoren in C ++ mit der Methode std :: vector :: swap auszutauschen?

30

Angenommen, Sie haben den folgenden Code:

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

Stellen Sie sich vor, der Vektor ist noch std::stringkeine Klasse:

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

Ist es immer noch sicher, die beiden Vektoren mit der std::vector::swapMethode zu tauschen : WidgetVector.swap(Widget2Vector);oder führt dies zu einer UB?

Emanuele Oggiano
quelle

Antworten:

20

Es ist sicher, da während des Swap-Vorgangs nichts erstellt wird. Es werden nur Datenelemente der Klasse std::vectorausgetauscht.

Betrachten Sie das folgende Demonstrationsprogramm, das deutlich macht, wie Objekte der Klasse std::vectorausgetauscht werden.

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

Die Programmausgabe ist

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 

Wie Sie sehen, werden nur Datenelemente ptrund nin der Elementfunktion ausgetauscht. Es werden keine zusätzlichen Ressourcen verwendet.

Ein ähnlicher Ansatz wird in der Klasse verwendet std::vector.

Wie für dieses Beispiel

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

dann gibt es Objekte verschiedener Klassen. Der Elementfunktionsaustausch wird auf Vektoren des gleichen Typs angewendet.

Vlad aus Moskau
quelle
6
Aber was ist mit dem tatsächlichen Fall des OP , in dem die ausgetauschten Vektoren verschiedenen Klassen angehören?
Adrian Mole
4
@AdrianMole Der Elementfunktionsaustausch, wenn er für den angegebenen Vektortyp definiert ist. Es ist nicht für Vektoren unterschiedlicher Typen definiert. Es ist keine Vorlagenelementfunktion.
Vlad aus Moskau
"Swap wird auf Vektoren des gleichen Typs angewendet" Sie sollten ein "nur" zwischen "ist" und "angewendet" hinzufügen.
SS Anne
Natürlich können zustandsbehaftete Allokatoren die Dinge ändern.
Deduplikator
21

Ja, dies ist absolut sicher, um Vektoren des gleichen Typs auszutauschen.

Der Vektor unter der Haube ist nur ein paar Zeiger, die auf die vom Vektor verwendeten Daten und das "Ende" der Sequenz verweisen. Wenn Sie Swap aufrufen, tauschen Sie einfach diese Zeiger zwischen den Vektoren aus. Sie müssen sich deshalb keine Sorgen machen, dass die Vektoren dieselbe Größe haben.

Vektoren unterschiedlicher Typen können nicht mit ausgetauscht werden swap. Sie müssten Ihre eigene Funktion implementieren, die die Konvertierung und den Austausch vornimmt.

NathanOliver
quelle
3
Sie müssen sich den zweiten Teil der Frage genauer ansehen.
Mark Ransom
@ MarkRansom Ja. Verpasste das 2. Aktualisiert.
NathanOliver
13

Ist es sicher, zwei verschiedene Vektoren in C ++ mit der Methode std :: vector :: swap auszutauschen?

Ja. Das Tauschen kann allgemein als sicher angesehen werden. Andererseits ist Sicherheit subjektiv und relativ und kann aus verschiedenen Perspektiven betrachtet werden. Daher ist es nicht möglich, eine zufriedenstellende Antwort zu geben, ohne die Frage mit einem Kontext zu ergänzen und zu entscheiden, welche Art von Sicherheit in Betracht gezogen wird.

Ist es immer noch sicher, die beiden Vektoren mit der Methode std :: vector :: swap auszutauschen: WidgetVector.swap (Widget2Vector); oder wird es zu einem UB führen?

Es wird keine UB geben. Ja, es ist immer noch sicher in dem Sinne, dass das Programm schlecht geformt ist.

Eerorika
quelle
7

Die swapFunktion ist wie folgt definiert : void swap( T& a, T& b );. Beachten Sie hier, dass beide aund vom selben Typb sind (und sein müssen) . (Mit dieser Signatur ist keine solche Funktion definiert : , da dies keinen Sinn ergeben würde!)void swap( T1& a, T2& b )

In ähnlicher Weise ist die swap()Elementfunktion der std::vectorKlasse wie folgt definiert:

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

Da es für den Funktionsparameter (der die Form :) hat, keine 'äquivalente' Definition mit einer Vorlagenüberschreibung gibt (siehe Explizite Spezialisierungen von Funktionsvorlagentemplate <typename T2> void swap(std::vector<T2>& other) ), muss dieser Parameter ein Vektor desselben Typs (Vorlage) sein wie die 'rufende' Klasse (das heißt, es muss auch eine sein vector<T1>).

Sie std::vector<Widget>und std::vector<Widget2>sind zwei verschiedene Typen, sodass der Aufruf von swapnicht kompiliert wird, unabhängig davon, ob Sie versuchen, die Elementfunktion eines Objekts (wie Ihr Code) oder die Spezialisierung der std::swap()Funktion zu verwenden, die zwei benötigtstd:vector Objekte als Parameter verwendet.

Adrian Mole
quelle
8
std::vector::swapist eine Mitgliedsfunktion, wie kann das eine Spezialisierung einer freistehenden Vorlagenfunktion sein ???
Aconcagua
1
Richtiges Ergebnis, falsche Argumentation.
NathanOliver
2
@AdrianMole Es gibt zwar eine Spezialisierung für std::swap, aber das ist nicht das, was das OP verwendet. Wenn Sie dies tun First.swap(Second);, rufen Sie an, std::vector::swapwas eine andere Funktion ist alsstd::swap
NathanOliver
1
Entschuldigung, mein schlechtes ... Ihr Link führt direkt zur Dokumentation der std :: swap-Spezialisierung für Vektoren. Dies unterscheidet sich jedoch von der Elementfunktion , die ebenfalls vorhanden ist und (nur) in Frage gestellt wird.
Aconcagua
2
Die Argumentation ist eigentlich analog: Das Element ist definiert als void swap(std::vector& other)(dh std::vector<T>), nicht als template <typename U> void swap(std::vector<U>& other)(unter der Annahme, dass T ein Typparameter für den Vektor selbst ist).
Aconcagua
4

Sie können keine Vektoren zweier verschiedener Typen austauschen, aber es handelt sich um einen Kompilierungsfehler anstelle von UB. vector::swapakzeptiert nur Vektoren des gleichen Typs und Allokators.

Sie sind sich nicht sicher, ob dies funktioniert, aber wenn Sie einen Vektor mit Widget2s von Widgets konvertieren möchten , können Sie Folgendes versuchen:

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

Widget2muss konstruierbar sein von Widget.

user233009
quelle
0

using std::swap; swap(a, b); und a.swap(b); haben genau die gleiche Semantik, wo letztere funktioniert; Zumindest für jeden vernünftigen Typ. Alle Standardtypen sind in dieser Hinsicht vernünftig.

Wenn Sie keinen interessanten Allokator verwenden (dh Stateful, nicht immer gleich und nicht beim Container-Swap weitergegeben, siehe std::allocator_traits), ist das Austauschen von zwei std::vectors mit denselben Template-Argumenten nur ein langweiliger Swap von drei Werten (für Kapazität, Größe und Daten-). Zeiger). Und das Austauschen von Basistypen ohne Datenrennen ist sicher und kann nicht geworfen werden.

Das garantiert sogar der Standard. Siehe std::vector::swap().

Deduplikator
quelle