使用 std::vector::swap 方法在C++中交换两个不同的向量是否安全?

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

本文关键字:两个 向量 安全 是否 方法 swap vector std C++ 使用 交换      更新时间:2023-10-16

>假设您有以下代码:

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

想象一下向量不是std::string,但是类:

std::vector<Widget> WidgetVector;
std::vector<Widget2> Widget2Vector;

std::vector::swap方法交换两个向量是否仍然安全:WidgetVector.swap(Widget2Vector);否则会导致 UB?

是的,交换相同类型的向量是完全安全的。

引擎盖下的向量只是指向向量使用的数据和序列"结束"的几个指针。 当你调用交换时,你只是在向量之间交换这些指针。 因此,您无需担心向量的大小相同。

不同类型的向量不能使用swap进行交换。 您需要实现自己的函数来进行转换和交换。

它是安全的,因为在交换操作期间不会创建任何内容。仅交换类std::vector的数据成员。

请考虑以下演示程序,该程序清楚地说明了如何交换类std::vector的对象。

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

程序输出为

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 

如您所见,在成员函数交换中,只有数据成员ptrn被交换。不会使用任何额外资源。

std::vector中使用了类似的方法。

至于这个例子

std::vector<Widget> WidgetVector;
std::vector<Widget2> Widget2Vector;

然后是不同类的对象。成员函数交换应用于相同类型的向量。

使用 std::vector::swap 方法在C++中交换两个不同的向量是否安全?

是的。交换通常被认为是安全的。另一方面,安全是主观的和相对的,可以从不同的角度考虑。因此,如果不用上下文来补充问题并选择正在考虑哪种安全性,就不可能给出令人满意的答案。

用 std::vector::swap 方法交换两个向量是否仍然安全:WidgetVector.swap(Widget2Vector(;或者它会导致 UB?

不会有 UB。是的,从程序格式不正确的意义上说,它仍然是安全的。

swap函数定义如下:void swap( T& a, T& b );.请注意,ab是(并且必须是(相同的类型。(没有用这个签名定义这样的函数:void swap( T1& a, T2& b ),因为它没有意义!

同样,std::vector类的swap()成员函数定义如下:

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

现在,由于函数参数(形式为:template <typename T2> void swap(std::vector<T2>& other)(没有模板覆盖的"等效"定义(请参阅函数模板的显式专用化(,该参数必须是与"调用"类相同类型(模板(的向量(即,它也必须是vector<T1>(。

std::vector<Widget>std::vector<Widget2>是两种不同的类型,因此,无论您是尝试使用任一对象的成员函数(如代码一样(,还是使用将两个std:vector对象作为参数的std::swap()函数的专用化,都不会编译对swap的调用。

您不能交换两种不同类型的向量,但这是编译错误而不是 UB。vector::swap只接受相同类型和分配器的向量。

不确定这是否有效,但如果您想要一个包含从Widgets 转换而来的Widget2s 的向量,您可以尝试以下操作:

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

Widget2必须从Widget移动可构造。

using std::swap; swap(a, b);a.swap(b);具有与后者完全相同的语义;至少对于任何理智的类型。在这方面,所有标准类型都是理智的。

除非你使用一个有趣的分配器(意味着有状态的,并不总是相等的,并且不会在容器交换时传播,请参阅std::allocator_traits(,否则使用相同的模板参数交换两个std::vector只是三个值(容量、大小和数据指针(的无聊交换。交换基本类型,没有数据竞争,是安全的,不能抛出。

这甚至是标准所保证的。请参阅std::vector::swap()