Reputation:
Suppose that you have the following 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;
}
Imagine the vector are not std::string
, yet classes:
std::vector<Widget> WidgetVector;
std::vector<Widget2> Widget2Vector;
Is it still safe to swap the two vectors with the std::vector::swap
method: WidgetVector.swap(Widget2Vector);
or it will lead to an UB?
Upvotes: 32
Views: 6421
Reputation: 51864
The swap
function is defined as follows: void swap( T& a, T& b );
. Note here that both a
and b
are (and have to be) the same type. (There is no such function defined with this signature: void swap( T1& a, T2& b )
, as it would make no sense!)
Similarly, the swap()
member function of the std::vector
class is defined as follows:
template<class T1> class vector // Note: simplified from the ACTUAL STL definition
{
//...
public:
void swap( vector& other );
//...
};
Now, as there is no 'equivalent' definition with a template override (see Explicit specializations of function templates) for the function parameter (which would be of the form: template <typename T2> void swap(std::vector<T2>& other)
), that parameter must be a vector of the same type (template) as the 'calling' class (that is, it must also be a vector<T1>
).
Your std::vector<Widget>
and std::vector<Widget2>
are two different types, so the call to swap
won't compile, whether you try to use the member function of either object (as your code does), or using the specialization of the std::swap()
function that takes two std:vector
objects as parameters.
Upvotes: 6
Reputation: 45674
using std::swap; swap(a, b);
and a.swap(b);
have the exact same semantics where the latter works; at least for any sane type. All standard types are sane in that regard.
Unless you use an interesting allocator (meaning stateful, not always equal, and not propagated on container swap, see std::allocator_traits
), swapping two std::vector
s with the same template-arguments is just a boring swap of three values (for capacity, size, and data-pointer). And swapping basic types, absent data-races, is safe and cannot throw.
That is even guaranteed by the standard. See std::vector::swap()
.
Upvotes: 0
Reputation: 311048
It is safe because nothing is created during the swap operation. Only data members of the class std::vector
are swapped.
Consider the following demonstrative program that makes it clear how objects of the class std::vector
are swapped.
#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;
}
The program output is
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
As you see only data members ptr
and n
are swapped in the member function swap. Neither additional resources are used.
A similar approach is used in the class std::vector
.
As for this example
std::vector<Widget> WidgetVector;
std::vector<Widget2> Widget2Vector;
then there are objects of different classes. The member function swap is applied to vectors of the same type.
Upvotes: 19
Reputation: 296
You can't swap vectors of two different types but it's a compilation error instead of UB. vector::swap
only accepts vectors of the same type and allocator.
Not sure if this will work, but if you want a vector containing Widget2
s converted from Widget
s you can try this:
std::vector<Widget2> Widget2Vector(
std::make_move_iterator(WidgetVector.begin()),
std::make_move_iterator(WidgetVector.end())
);
Widget2
will have to be move constructible from Widget
.
Upvotes: 4
Reputation: 180935
Yes, this is perfectly safe to swap vectors of the same type.
Vector under the hood is just a few pointers pointing to the data the vector uses and "end" of the sequence. When you call swap you just exchange those pointers between the vectors. You don't need to worry that the vectors are the same size because of this.
Vectors of different types cannot be swapped using swap
. You'd need to implement your own function that does the conversion and swapping.
Upvotes: 23
Reputation: 238401
Is it safe to swap two different vectors in C++, using the std::vector::swap method?
Yes. Swapping can generally be considered safe. On the other hand, safety is subjective and relative and can be considered from different perspectives. As such, it is not possible to give a satisfactory answer without augmenting the question with a context, and choosing what sort of safety is being considered.
Is it still safe to swap the two vectors with the std::vector::swap method: WidgetVector.swap(Widget2Vector); or it will lead to an UB?
There will not be UB. Yes, it is still safe in the sense that the program is ill-formed.
Upvotes: 13