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


30

假设您具有以下代码:

#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?

Answers:


20

这是安全的,因为在交换操作期间不会创建任何内容。仅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 

正如你看到的只是数据成员ptr,并n在成员函数交换的交换。既不使用其他资源。

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

至于这个例子

std::vector<Widget> WidgetVector;

std::vector<Widget2> Widget2Vector;

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


6
但是,OP的实际情况是什么,交换的向量属于不同的类?
Adrian Mole

4
@AdrianMole如果为向量的给定类型定义了成员函数交换。没有为不同类型的向量定义。它不是模板成员函数。
弗拉德(Flad)来自莫斯科,

“交换应用于相同类型的向量”您应在“ is”和“ applied”之间添加“ only”。
SS安妮

当然,有状态分配器可能会改变事情。
Deduplicator

21

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

底层的向量只是一些指向向量使用的数据和序列“结尾”的指针。调用swap时,您只需在向量之间交换这些指针。因此,您不必担心向量的大小相同。

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


3
您需要仔细研究问题的第二部分。
Mark Ransom

@MarkRansom是的。错过了2。更新。
NathanOliver

13

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

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

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

不会有UB。是的,从某种意义上来说程序是错误的,这仍然是安全的。


7

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

类似地,该类的swap()成员函数std::vector定义如下:

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


8
std::vector::swap是成员函数,那怎么可以是独立模板函数的专业化呢???
阿空加瓜

1
正确的结果,错误的推理。
NathanOliver

2
@AdrianMole虽然确实存在的专门化std::swap,但这不是OP所使用的。完成后First.swap(Second);,您将调用std::vector::swapstd::swap
NathanOliver

1
抱歉,我不好。您的链接直接指向vector的std :: swap专业化文档。但这不同于成员函数,后者也存在并且仅在有问题的情况下使用。
阿空加瓜

2
推理实际上是类似的:成员定义为void swap(std::vector& other)(即std::vector<T>),而不是template <typename U> void swap(std::vector<U>& other)(假设T是向量本身的类型参数)。
阿空加瓜

4

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

不知道这是否行得通,但是如果您想要包含Widget2Widgets 转换为s 的向量,则可以尝试以下操作:

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

Widget2将必须从构造可移动Widget


0

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

除非您使用有趣的分配器(表示状态的,并非总是相等的,并且不会在容器交换中传播,请参阅std::allocator_traits参考资料),否则std::vector用相同的模板参数交换两个s只是三个值(对于容量,大小和数据的无聊交换),指针)。交换基本类型(缺少数据争用)是安全的并且不能抛出。

该标准甚至保证了这一点。请参阅std::vector::swap()

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.