std::vector
クラス内にデータメンバーがあるコードをテストしていました。このクラスはコピー可能で移動可能であり、コピーアンドスワップイディオムを使用してここでoperator=
説明するように実装されます。
たとえば、大容量と小容量の2つvector
のがあり、 ()にコピーされた場合、の大容量は割り当て後も保持されます。次の呼び出しで新しい再割り当てを強制する必要がないため、これは理にかなっています(つまり、すでに使用可能なメモリを解放してから、ベクトルを拡張するために再割り当てしてもあまり意味がありません)。v1
v2
v2
v1
v1 = v2
v1
v1.push_back()
ただし、データメンバーとしてのクラスで同じ割り当てを行うとvector
、動作が異なり、割り当て後はより大きな容量が維持されません。
コピーアンドスワップイディオムが使用されておらず、コピーoperator=
と移動が別々operator=
に実装されている場合、動作は期待どおりです(通常の非メンバーの場合と同様)。vector
何故ですか?最適なパフォーマンスを得るために、コピーアンドスワップのイディオムに従い、代わりにoperator=(const X& other)
(コピー op=
)とoperator=(X&& other)
(移動)を別々に実装する必要がありますか? op=
これは、コピーアンドスワップイディオムを使用した再現可能なテストの出力です(この場合、後x1 = x2
はx1.GetV().capacity()
1,000,000ではなく1,000になることに注意してください )。
C:\TEMP\CppTests>cl /EHsc /W4 /nologo /DTEST_COPY_AND_SWAP test.cpp test.cpp C:\TEMP\CppTests>test.exe v1.capacity() = 1000000 v2.capacity() = 1000 After copy v1 = v2: v1.capacity() = 1000000 v2.capacity() = 1000 [Copy-and-swap] x1.GetV().capacity() = 1000000 x2.GetV().capacity() = 1000 After x1 = x2: x1.GetV().capacity() = 1000 x2.GetV().capacity() = 1000
これは、コピーアンドスワップのイディオムを使用しないx1.GetV().capacity() = 1000000
出力です(この場合、予想どおりに注意してください)。
C:\TEMP\CppTests>cl /EHsc /W4 /nologo test.cpp test.cpp C:\TEMP\CppTests>test.exe v1.capacity() = 1000000 v2.capacity() = 1000 After copy v1 = v2: v1.capacity() = 1000000 v2.capacity() = 1000 [Copy-op= and move-op=] x1.GetV().capacity() = 1000000 x2.GetV().capacity() = 1000 After x1 = x2: x1.GetV().capacity() = 1000000 x2.GetV().capacity() = 1000
コンパイル可能なサンプルコードは次のとおりです(VS2010 SP1 / VC10でテスト済み)。
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
class X
{
public:
X()
{
}
explicit X(const size_t initialCapacity)
{
m_v.reserve(initialCapacity);
}
X(const X& other)
: m_v(other.m_v)
{
}
X(X&& other)
: m_v(move(other.m_v))
{
}
void SetV(const vector<double>& v)
{
m_v = v;
}
const vector<double>& GetV() const
{
return m_v;
}
#ifdef TEST_COPY_AND_SWAP
//
// Implement a unified op= with copy-and-swap idiom.
//
X& operator=(X other)
{
swap(*this, other);
return *this;
}
friend void swap(X& lhs, X& rhs)
{
using std::swap;
swap(lhs.m_v, rhs.m_v);
}
#else
//
// Implement copy op= and move op= separately.
//
X& operator=(const X& other)
{
if (this != &other)
{
m_v = other.m_v;
}
return *this;
}
X& operator=(X&& other)
{
if (this != &other)
{
m_v = move(other.m_v);
}
return *this;
}
#endif
private:
vector<double> m_v;
};
// Test vector assignment from a small vector to a vector with big capacity.
void Test1()
{
vector<double> v1;
v1.reserve(1000*1000);
vector<double> v2(1000);
cout << "v1.capacity() = " << v1.capacity() << '\n';
cout << "v2.capacity() = " << v2.capacity() << '\n';
v1 = v2;
cout << "\nAfter copy v1 = v2:\n";
cout << "v1.capacity() = " << v1.capacity() << '\n';
cout << "v2.capacity() = " << v2.capacity() << '\n';
}
// Similar to Test1, but now vector is a data member inside a class.
void Test2()
{
#ifdef TEST_COPY_AND_SWAP
cout << "[Copy-and-swap]\n\n";
#else
cout << "[Copy-op= and move-op=]\n\n";
#endif
X x1(1000*1000);
vector<double> v2(1000);
X x2;
x2.SetV(v2);
cout << "x1.GetV().capacity() = " << x1.GetV().capacity() << '\n';
cout << "x2.GetV().capacity() = " << x2.GetV().capacity() << '\n';
x1 = x2;
cout << "\nAfter x1 = x2:\n";
cout << "x1.GetV().capacity() = " << x1.GetV().capacity() << '\n';
cout << "x2.GetV().capacity() = " << x2.GetV().capacity() << '\n';
}
int main()
{
Test1();
cout << '\n';
Test2();
}