web-dev-qa-db-ja.com

STLベクトル:ベクトルのすべての要素を移動する

2つのSTLベクトルABがあり、Aのすべての要素をクリアし、Bのすべての要素をAその後、Bをクリアします。簡単に言えば、私はこれをやりたい:

std::vector<MyClass> A;
std::vector<MyClass> B;
....
A = B;
B.clear();

Bはかなり長い可能性があるため、この操作を行うにはk*O(N)が必要です。ここで、kは定数で、Nmax(size_of(A), size_of(B))です。 。もっと効率的な方法があるのではないかと思っていました。考えられることの1つは、ABをポインターとして定義し、一定時間でポインターをコピーしてBをクリアすることです。

44
aminfar

C++ 11を使用すると、次のように簡単です。

_A = std::move(B);
_

Aには、以前Bによって保持されていた要素が含まれ、Bは空になりました。これにより、コピーが回避されます。内部表現はBからAに移動されるだけなので、これはO(1)ソリューションです。

C++ 03に関しては、プレトリアンが述べているように、ベクトルを交換できます。 _std::swap_ sを引数として取る_std::vector_関数の特殊化があります。これにより、内部表現が効果的に交換されるため、それらが保持する要素のコピーの作成を回避できます。この関数は、O(1)複雑度でも機能します。

70
mfontanini

C++ 11コンパイラを使用している場合は、BAに移動できます。

A = std::move(B);

古いコンパイラを使用している場合は、swap

A.swap(B);

どちらの場合でも、唯一のO(N)操作はAの内容をクリアします。最初のケースでは割り当て自体の間にクリアが行われますが、2番目のケースではBがスコープから外れたときに(内容がスワップされたため)クリアされます。

13
Praetorian

2つのSTLベクトルAとBがあり、Aのすべての要素をクリアし、Bのすべての要素をAに移動してからBをクリアしたいと思います。

これは、swapの組み合わせで実行できます。まず、前半のABを交換します。 swap空の_std::vector<>_でBを使用するか、clear()を呼び出します。違いは、clear()はメモリを解放せず、オブジェクトを破壊するだけです:

_std::vector<int> a, b; // initialize them somehow
swap(a,b);

// clear b without releasing the memory:
std::size_t capacity = b.capacity();
b.clear();
assert(b.capacity()==capacity);

// or release the memory
std::vector<int>().swap(b);
assert(b.capacity()==0);
_

単にclearを呼び出すと、o(1)時間がかかります。clearは何もしないので、Aに割り当てた後にBを本当にクリアしたい場合は、次のようにできます。

A.swap(B);
{
    std::Vector<..> C;
    c.swap(B);
}
3
mogulkahn

スワップ機能はこれを行います。

#include <iostream>
#include <iterator>
#include <vector>

int main(int argc, char* argv)
{
  std::vector<int> A;
  std::vector<int> B;

  for (int i = 0; i < 10; ++i)
  {
     B.Push_back(i);
  }

  std::cout << "Before swap\n";
  std::cout << "A:";
  std::copy(A.begin(), A.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\nB:";
  std::copy(B.begin(), B.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\n";

  A.swap(B);
  B.clear();

  std::cout << "After swap\n";
  std::cout << "A:";
  std::copy(A.begin(), A.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\nB:";
  std::copy(B.begin(), B.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\n";
}

出力

Before swap
A:
B:0 1 2 3 4 5 6 7 8 9 
After swap
A:0 1 2 3 4 5 6 7 8 9 
B:
2
Jason

std :: moveは正常に動作します。同じもののサンプルコードを次に示します。

    vector<int> v1 = {1,2,3,10,20,30,100,200,300,999};
    vector<int> v2;

    cout << "Size of v1 before move = " << v1.size() << endl;
    cout << "Capacity of v1 before move = " << v1.capacity() << endl;

    v2 = std::move(v1);

    cout << "Size of v2 after move = " << v2.size() << endl;
    cout << "Capacity of v2 after move = " << v2.capacity() << endl;

    cout << "Size of v1 after move = " << v1.size() << endl;
    cout << "Capacity of v1 after move = " << v1.capacity() << endl;

-----------Output-------------------------
Size of v1 before move = 10
Capacity of v1 before move = 10
Size of v2 after move = 10
Capacity of v2 after move = 10
Size of v1 after move = 0
Capacity of v1 after move = 0
2
Sameer Ahuja

ベクトルをstd :: moveまたはstd :: swapできない場合(たとえば、AとBは関連しているが異なるタイプ、おそらくconstだけが異なるため)、次のことができます:

std::vector<MyClass>       A;
std::vector<const MyClass> B;
// ...
for( auto& a : A )
{
    B.emplace_back( std::move( a ) );
}

これにより、Aには同じ数の要素が残りますが、それらはすべて不確定な状態にあることに注意してください(つまり、それらを割り当てたり、破壊したりすることはできますが、読み取ることはできません)。

1
metal

コメントする担当者はいませんが、次のとおりです。 https://en.cppreference.com/w/cpp/container/vector/operator%3D void.pointer is right。特に...

2)割り当て演算子を移動します。移動セマンティクスを使用して、コンテンツを他のコンテンツに置き換えます(つまり、otherのデータはotherからこのコンテナーに移動されます)。その他は、その後は有効ですが指定されていない状態です。

このように、Praetorianの答えは規格ごとに間違っています。ただし、少なくともMSVCについては、実装によってリストがクリアされるため十分です(おそらくほとんどの場合に当てはまります)。

興味深いのは、ムーブコンストラクターを宣言しているため、暗黙的なムーブ割り当て演算子が宣言されないことです。したがって、std :: vectorは移動代入演算子を宣言する必要があることを「知っています」。

0