使用 std::vector::swap 方法在 C++ 中交换两个不同的向量是否安全?
Is it safe to swap two different vectors in C++, using the std::vector::swap method?
假设您有以下代码:
#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?
是的,交换相同类型的向量是绝对安全的。
引擎盖下的向量只是指向向量使用的数据和序列的 "end" 的几个指针。当您调用交换时,您只需在向量之间交换这些指针。因此,您无需担心矢量大小相同。
无法使用 swap
交换不同类型的向量。您需要实现自己的函数来进行转换和交换。
Is it safe to swap two different vectors in C++, using the std::vector::swap method?
是的。交换通常可以被认为是安全的。另一方面,安全是主观的、相对的,可以从不同的角度考虑。因此,如果不通过上下文扩充问题并选择正在考虑的安全类型,就不可能给出令人满意的答案。
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?
不会有UB。是的,从程序格式错误的意义上来说它仍然是安全的。
swap
函数定义如下:void swap( T& a, T& b );
。请注意,a
和 b
都是(并且 有 是) 相同的类型 。 (没有用这个签名定义这样的函数:void swap( T1& a, T2& b )
,因为它没有意义!)
同理,std::vector
class的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)
)没有带有模板覆盖(参见 Explicit specializations of function templates)的 'equivalent' 定义,该参数 必须是 与 'calling' class 相同类型(模板)的向量(也就是说,它也必须是 vector<T1>
)。
你的std::vector<Widget>
和std::vector<Widget2>
是两个不同的类型,所以调用swap
将无法编译,无论您尝试使用任一对象的成员函数(如您的代码所做的那样),还是使用需要两个 std::swap()
函数的 specialization std:vector
对象作为参数。
您不能交换两种不同类型的向量,但这是一个编译错误,而不是 UB。 vector::swap
只接受相同类型和分配器的向量。
不确定这是否可行,但如果你想要一个包含从 Widget
s 转换而来的 Widget2
s 的向量,你可以试试这个:
std::vector<Widget2> Widget2Vector(
std::make_move_iterator(WidgetVector.begin()),
std::make_move_iterator(WidgetVector.end())
);
Widget2
必须从 Widget
.
移动可构造
这是安全的,因为在交换操作期间没有创建任何东西。仅交换 class std::vector
的数据成员。
考虑以下演示程序,它清楚地说明了 class 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
如你所见,成员函数swap中只交换了数据成员ptr
和n
。没有使用额外的资源。
class std::vector
.
中使用了类似的方法
至于这个例子
std::vector<Widget> WidgetVector;
std::vector<Widget2> Widget2Vector;
然后有不同的对象 classes。成员函数 swap 应用于相同类型的向量。
using std::swap; swap(a, b);
和 a.swap(b);
在后者工作的地方具有完全相同的语义;至少对于任何理智的类型。在这方面,所有标准类型都是理智的。
除非你使用一个有趣的分配器(意思是有状态的,不总是相等的,并且不会在容器交换中传播,请参阅 std::allocator_traits
), swapping two std::vector
s 具有相同的模板参数只是三个值的无聊交换(对于容量、大小和数据指针)。交换基本类型,没有数据竞争,是安全的,不会抛出。
标准甚至保证了这一点。参见 std::vector::swap()
。
假设您有以下代码:
#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?
是的,交换相同类型的向量是绝对安全的。
引擎盖下的向量只是指向向量使用的数据和序列的 "end" 的几个指针。当您调用交换时,您只需在向量之间交换这些指针。因此,您无需担心矢量大小相同。
无法使用 swap
交换不同类型的向量。您需要实现自己的函数来进行转换和交换。
Is it safe to swap two different vectors in C++, using the std::vector::swap method?
是的。交换通常可以被认为是安全的。另一方面,安全是主观的、相对的,可以从不同的角度考虑。因此,如果不通过上下文扩充问题并选择正在考虑的安全类型,就不可能给出令人满意的答案。
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?
不会有UB。是的,从程序格式错误的意义上来说它仍然是安全的。
swap
函数定义如下:void swap( T& a, T& b );
。请注意,a
和 b
都是(并且 有 是) 相同的类型 。 (没有用这个签名定义这样的函数:void swap( T1& a, T2& b )
,因为它没有意义!)
同理,std::vector
class的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)
)没有带有模板覆盖(参见 Explicit specializations of function templates)的 'equivalent' 定义,该参数 必须是 与 'calling' class 相同类型(模板)的向量(也就是说,它也必须是 vector<T1>
)。
你的std::vector<Widget>
和std::vector<Widget2>
是两个不同的类型,所以调用swap
将无法编译,无论您尝试使用任一对象的成员函数(如您的代码所做的那样),还是使用需要两个 std::swap()
函数的 specialization std:vector
对象作为参数。
您不能交换两种不同类型的向量,但这是一个编译错误,而不是 UB。 vector::swap
只接受相同类型和分配器的向量。
不确定这是否可行,但如果你想要一个包含从 Widget
s 转换而来的 Widget2
s 的向量,你可以试试这个:
std::vector<Widget2> Widget2Vector(
std::make_move_iterator(WidgetVector.begin()),
std::make_move_iterator(WidgetVector.end())
);
Widget2
必须从 Widget
.
这是安全的,因为在交换操作期间没有创建任何东西。仅交换 class std::vector
的数据成员。
考虑以下演示程序,它清楚地说明了 class 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
如你所见,成员函数swap中只交换了数据成员ptr
和n
。没有使用额外的资源。
class std::vector
.
至于这个例子
std::vector<Widget> WidgetVector;
std::vector<Widget2> Widget2Vector;
然后有不同的对象 classes。成员函数 swap 应用于相同类型的向量。
using std::swap; swap(a, b);
和 a.swap(b);
在后者工作的地方具有完全相同的语义;至少对于任何理智的类型。在这方面,所有标准类型都是理智的。
除非你使用一个有趣的分配器(意思是有状态的,不总是相等的,并且不会在容器交换中传播,请参阅 std::allocator_traits
), swapping two std::vector
s 具有相同的模板参数只是三个值的无聊交换(对于容量、大小和数据指针)。交换基本类型,没有数据竞争,是安全的,不会抛出。
标准甚至保证了这一点。参见 std::vector::swap()
。