user11121949
user11121949

Reputation:

Is it safe to swap two different vectors in C++, using the std::vector::swap method?

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

Answers (6)

Adrian Mole
Adrian Mole

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

Deduplicator
Deduplicator

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::vectors 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

Vlad from Moscow
Vlad from Moscow

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

user233009
user233009

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 Widget2s converted from Widgets 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

NathanOliver
NathanOliver

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

eerorika
eerorika

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

Related Questions