std :: vector :: swapメソッドを使用して、C ++で2つの異なるベクトルを交換しても安全ですか?


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;

2つのベクトルをstd::vector::swapメソッドで交換しても安全WidgetVector.swap(Widget2Vector);ですか?それともUBにつながりますか?

回答:


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;

次に、異なるクラスのオブジェクトがあります。メンバー関数swapは、同じタイプのベクトルに適用されます。


6
しかし、OPの実際の場合、スワップされるベクトルが異なるクラスである場合はどうでしょうか。
Adrian Mole、

4
@AdrianMoleメンバー関数swapは、指定されたタイプのベクトルに対して定義されている場合に交換されます。異なるタイプのベクターに対しては定義されていません。テンプレートメンバー関数ではありません。
モスクワ出身のヴラド、

「スワップは同じタイプのベクターに適用されます」「is」と「appplied」の間に「only」を追加する必要があります。
SSアン

もちろん、ステートフルなアロケーターは状況を変えるかもしれません。
Deduplicator

21

はい、これは同じタイプのベクターを交換するのに完全に安全です。

ボンネットの下のベクターは、ベクターが使用するデータと、シーケンスの「終わり」を指すポインタの一部です。swapを呼び出すときは、ベクトル間でこれらのポインターを交換するだけです。このため、ベクトルが同じサイズであることを心配する必要はありません。

を使用して、異なるタイプのベクターを交換することはできませんswap。変換と交換を行う独自の関数を実装する必要があります。


3
質問の2番目の部分をさらに詳しく見る必要があります。
Mark Ransom

@MarkRansomうん。逃した2。更新しました。
NathanOliver

13

std :: vector :: swapメソッドを使用して、C ++で2つの異なるベクトルを交換しても安全ですか?

はい。スワッピングは一般的に安全と見なすことができます。一方、安全性は主観的かつ相対的であり、さまざまな視点から考えることができます。そのため、質問を文脈で補強し、どのような安全性を検討するかを選択しないと、満足のいく答えを出すことはできません。

2つのベクトルをstd :: vector :: swapメソッドで交換しても安全ですか?WidgetVector.swap(Widget2Vector); それともUBにつながりますか?

UBはありません。はい、プログラムの形式が正しくないという意味では、まだ安全です。


7

swapこの関数は次のように定義されますvoid swap( T& a, T& b );。両方のことをここで注意aしてbいる(と持っているように)同じタイプ。(このシグネチャで定義された関数はありません: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>の2つのです異なるタイプのため、コールがしswapます(あなたのコードが行うように)のいずれかのオブジェクトのメンバ関数を使用しようとするかどうか、コンパイル、または使用しないであろう専門std::swap()2つの取る関数std:vectorのパラメータとしてオブジェクトを。


8
std::vector::swapはメンバー関数ですが、それはどのようにして独立型テンプレート関数の特殊化になるのでしょうか?
アコンカグア

1
正しい結果、間違った推論。
NathanOliver

2
@AdrianMoleの特殊化は存在しますがstd::swap、それはOPが使用しているものではありません。するとFirst.swap(Second);、次のstd::vector::swap関数とは異なる関数を呼び出しますstd::swap
NathanOliver

1
申し訳ありませんが、私の悪い...あなたのリンクは、ベクトルのstd :: swap特殊化のドキュメントに直接行きます。ただし、これは、存在し、問題の(のみ)で使用されるメンバー関数とは異なります。
アコンカグア

2
推論は実際には類似しています:メンバーは(Tがベクトル自体の型パラメーターであると仮定して)ではなくvoid swap(std::vector& other)(つまりstd::vector<T>)として定義されtemplate <typename U> void swap(std::vector<U>& other)ます。
アコンカグア

4

2つの異なるタイプのベクトルを交換することはできませんが、UBではなくコンパイルエラーです。vector::swap同じタイプとアロケーターのベクトルのみを受け入れます。

これが機能するかどうかはわかりませんが、Widget2sから変換されたWidgetsを含むベクトルが必要な場合は、これを試すことができます。

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同じテンプレートで2つのをスワップすることは、3つの値(容量、サイズ、およびデータ)の退屈なスワップにすぎませんポインタ)。また、データ競合がない場合でも基本的なタイプを交換することは安全であり、スローできません。

それは規格によっても保証されています。を参照してくださいstd::vector::swap()

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.