Jest bezpieczny, ponieważ nic nie jest tworzone podczas operacji wymiany. std::vector
Wymieniani są tylko członkowie danych klasy .
Rozważ następujący program demonstracyjny, który wyjaśnia, w jaki sposób std::vector
zamieniane są obiekty klasy .
#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;
}
Wyjście programu to
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
Jak widać tylko członków danych ptr
i n
są zamieniani w funkcji zamiany członka. Żadne dodatkowe zasoby nie są używane.
Podobne podejście stosuje się w klasie std::vector
.
Jak w tym przykładzie
std::vector<Widget> WidgetVector;
std::vector<Widget2> Widget2Vector;
są też obiekty różnych klas. Zamiana funkcji składowej jest stosowana do wektorów tego samego typu.
Tak, jest to całkowicie bezpieczne podczas wymiany wektorów tego samego typu.
Wektor pod maską to tylko kilka wskaźników wskazujących dane używane przez wektor i „koniec” sekwencji. Kiedy wywołujesz zamianę, po prostu wymieniasz te wskaźniki między wektorami. Z tego powodu nie musisz się martwić, że wektory są tego samego rozmiaru.
Wektory różnych typów nie mogą być zamieniane za pomocą
swap
. Musisz zaimplementować własną funkcję, która wykonuje konwersję i zamianę.źródło
2
. ZaktualizowanoTak. Zamiana może być ogólnie uważana za bezpieczną. Z drugiej strony bezpieczeństwo jest subiektywne i względne i można je rozpatrywać z różnych perspektyw. W związku z tym nie można udzielić satysfakcjonującej odpowiedzi bez rozszerzenia pytania o kontekst i wybrania rodzaju rozważanego bezpieczeństwa.
Nie będzie UB. Tak, nadal jest bezpieczny, ponieważ program jest źle sformułowany.
źródło
swap
Funkcja jest określona w następujący sposób:void swap( T& a, T& b );
. Zauważmy, że obaa
ib
są (i muszą być) ten sam typ . (Nie ma takiej funkcji zdefiniowanej z tym podpisem:void swap( T1& a, T2& b )
ponieważ nie miałoby to sensu!)Podobnie
swap()
funkcjastd::vector
składowa klasy jest zdefiniowana następująco:Ponieważ nie istnieje „równoważna” definicja z nadpisaniem szablonu (patrz Jawna specjalizacja szablonów funkcji ) dla parametru funkcji (który miałby postać
template <typename T2> void swap(std::vector<T2>& other)
:), parametr ten musi być wektorem tego samego typu (szablonu) co klasa „wzywająca” (tzn. musi to być również avector<T1>
).Twój
std::vector<Widget>
istd::vector<Widget2>
są dwa różne typy, więc wywołanieswap
nie zostanie skompilowany, czy spróbować użyć funkcji składowej albo obiektu (jak kod robi), lub za pomocą specjalizację zstd::swap()
funkcji, która trwa dwastd:vector
obiekty jako parametry.źródło
std::vector::swap
jest funkcją składową, jak to może być specjalizacja wolnostojącej funkcji szablonu?std::swap
, to nie jest to, czego używa OP. Kiedy to zrobiszFirst.swap(Second);
, dzwonisz,std::vector::swap
co jest inną funkcją niżstd::swap
void swap(std::vector& other)
(tj.std::vector<T>
), A nie jakotemplate <typename U> void swap(std::vector<U>& other)
(zakładając, że T jest parametrem typu dla samego wektora).Nie można zamienić wektorów dwóch różnych typów, ale jest to błąd kompilacji zamiast UB.
vector::swap
akceptuje tylko wektory tego samego typu i alokatora.Nie jestem pewien, czy to zadziała, ale jeśli chcesz
Widget2
przekonwertować wektor zawierający s zWidget
s, możesz spróbować:Widget2
będzie musiał być możliwy do przeniesieniaWidget
.źródło
using std::swap; swap(a, b);
ia.swap(b);
mają dokładnie taką samą semantykę, w której działa ta druga; przynajmniej dla każdego rozsądnego typu. Wszystkie standardowe typy są pod tym względem rozsądne.O ile nie użyjesz interesującego alokatora (co oznacza stanowe, nie zawsze równe i nie propagowane podczas wymiany kontenera, zobacz
std::allocator_traits
), zamiana dwóchstd::vector
s za pomocą tych samych szablonów-argumentów jest po prostu nudną zamianą trzech wartości (dla pojemności, wielkości i danych wskaźnik). A zamiana podstawowych typów, nieobecnych w wyścigach danych, jest bezpieczna i nie można jej rzucać.Jest to nawet gwarantowane przez standard. Zobaczyć
std::vector::swap()
.źródło