2つのSTLベクトルA
とB
があり、A
のすべての要素をクリアし、B
のすべての要素をA
その後、B
をクリアします。簡単に言えば、私はこれをやりたい:
std::vector<MyClass> A;
std::vector<MyClass> B;
....
A = B;
B.clear();
B
はかなり長い可能性があるため、この操作を行うにはk*O(N)
が必要です。ここで、k
は定数で、N
はmax(size_of(A), size_of(B))
です。 。もっと効率的な方法があるのではないかと思っていました。考えられることの1つは、A
とB
をポインターとして定義し、一定時間でポインターをコピーしてB
をクリアすることです。
C++ 11を使用すると、次のように簡単です。
_A = std::move(B);
_
A
には、以前B
によって保持されていた要素が含まれ、B
は空になりました。これにより、コピーが回避されます。内部表現はB
からA
に移動されるだけなので、これはO(1)
ソリューションです。
C++ 03に関しては、プレトリアンが述べているように、ベクトルを交換できます。 _std::swap
_ sを引数として取る_std::vector
_関数の特殊化があります。これにより、内部表現が効果的に交換されるため、それらが保持する要素のコピーの作成を回避できます。この関数は、O(1)
複雑度でも機能します。
C++ 11コンパイラを使用している場合は、B
をA
に移動できます。
A = std::move(B);
古いコンパイラを使用している場合は、swap
A.swap(B);
どちらの場合でも、唯一のO(N)操作はA
の内容をクリアします。最初のケースでは割り当て自体の間にクリアが行われますが、2番目のケースではB
がスコープから外れたときに(内容がスワップされたため)クリアされます。
2つのSTLベクトルAとBがあり、Aのすべての要素をクリアし、Bのすべての要素をAに移動してからBをクリアしたいと思います。
これは、swap
の組み合わせで実行できます。まず、前半のA
とB
を交換します。 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);
}
スワップ機能はこれを行います。
#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:
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
ベクトルを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には同じ数の要素が残りますが、それらはすべて不確定な状態にあることに注意してください(つまり、それらを割り当てたり、破壊したりすることはできますが、読み取ることはできません)。
コメントする担当者はいませんが、次のとおりです。 https://en.cppreference.com/w/cpp/container/vector/operator%3D void.pointer is right。特に...
2)割り当て演算子を移動します。移動セマンティクスを使用して、コンテンツを他のコンテンツに置き換えます(つまり、otherのデータはotherからこのコンテナーに移動されます)。その他は、その後は有効ですが指定されていない状態です。
このように、Praetorianの答えは規格ごとに間違っています。ただし、少なくともMSVCについては、実装によってリストがクリアされるため十分です(おそらくほとんどの場合に当てはまります)。
興味深いのは、ムーブコンストラクターを宣言しているため、暗黙的なムーブ割り当て演算子が宣言されないことです。したがって、std :: vectorは移動代入演算子を宣言する必要があることを「知っています」。