Move semantics に対応した Boost.Containersとは?
ということで、再びBoost.Containerに戻ってきました。
(コピーコンストラクタと、ムーブコンストラクタについてよくわからない場合は前の記事へ)
ムーブセマンティクスに対応する、というのはどういうことかを考えます。
では、ムーブコンストラクタに対応したクラスをBoost.Containerを利用して定義します。
class MoveSemanticsObject { BOOST_MOVABLE_BUT_NOT_COPYABLE(MoveSemanticsObject) public: MoveSemanticsObject() { cout << "MoveSemanticsObject::MoveSemanticsObject()" << endl; } ~MoveSemanticsObject() { cout << "MoveSemanticsObject::~MoveSemanticsObject()" << endl; } MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject)) { } MoveSemanticsObject& operator=(BOOST_RV_REF(MoveSemanticsObject)) { return *this; } };
C++03には右辺値参照はありませんので、
BOOST_RV_REF
がrvalue referenceをエミュレーションしたムーブコンストラクタをつくってくれます。
#ifdef BOOST_HAS_RVALUE_REFS #define BOOST_RV_REF(TYPE) TYPE && #else #define BOOST_RV_REF(TYPE) boost::rv< TYPE >& #endif
こんな感じ。
&& がC++ 0xで正式に対応されるムーブコンストラクタのカタチですが、今は無視してください。
rvalue referenceとかキニシナイ!
で、
typedef boost::container::vector<MoveSemanticsObject> BoostVectorMoveSemanticsObjectList; int main() { BoostVectorMoveSemanticsObjectList v; MoveSemanticsObject m1; MoveSemanticsObject m2; v.push_back(boost::move(m1)); v.push_back(boost::move(m2)); return 0; }
実行。
MoveSemanticsObject::MoveSemanticsObject()
MoveSemanticsObject::MoveSemanticsObject()
MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject))
MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject))
MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject))
MoveSemanticsObject::~MoveSemanticsObject()
MoveSemanticsObject::~MoveSemanticsObject()
MoveSemanticsObject::~MoveSemanticsObject()
MoveSemanticsObject::~MoveSemanticsObject()
MoveSemanticsObject::~MoveSemanticsObject()
vectorが拡張(リアロケーション)される際に「コピーコンストラクタ」ではなく
「ムーブコンストラクタ」である
MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject))
が呼ばれていますね。
これは「ムーブコンストラクタ」なので所有権を移動すればそれで構いません。
ということは、
vectorが拡張する際に
「複製を大量に作らなければならないコスト」
を抑え、
「所有権を移動するコスト」
だけにすることができます。
では、所有権の移動テスト。
valueの所有権をMoveSemanticsObjectに持たせ、それがどのように移動するかを示したコードです。
#include <boost/container/vector.hpp> #include <boost/move/move.hpp> namespace { int uniqueNumber_ = 100; } class MoveSemanticsObject { BOOST_MOVABLE_BUT_NOT_COPYABLE(MoveSemanticsObject) public: MoveSemanticsObject() : valueOwnership_(new int(uniqueNumber_++)) { cout << "MoveSemanticsObject::MoveSemanticsObject()" << endl; } ~MoveSemanticsObject() { cout << "MoveSemanticsObject::~MoveSemanticsObject(ownership:" << (hasOwnership() ? "YES" : "NO") << ")" << endl; delete valueOwnership_; } MoveSemanticsObject(const MoveSemanticsObject& rhs) { cout << "MoveSemanticsObject(const MoveSemanticsObject& rhs)" << endl; } MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject) rhs) : valueOwnership_(rhs.valueOwnership_) { cout << "MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject))" << endl; rhs.valueOwnership_ = 0; } MoveSemanticsObject& operator=(BOOST_RV_REF(MoveSemanticsObject) rhs) { cout << "MoveSemanticsObject& operator=(BOOST_RV_REF(MoveSemanticsObject))" << endl; valueOwnership_ = rhs.valueOwnership_; rhs.valueOwnership_ = 0; return *this; } int value() const { return *valueOwnership_; } bool hasOwnership() const { return valueOwnership_ != 0; } private: int* valueOwnership_; }; typedef boost::container::vector<MoveSemanticsObject> BVMOList; typedef BVMOList::iterator BVMOListIt; int main() { BVMOList v; MoveSemanticsObject m1; MoveSemanticsObject m2; MoveSemanticsObject m3; MoveSemanticsObject m4; cout << "----- 1st push_back -----" << endl; v.push_back(boost::move(m1)); cout << "----- 2nd push_back -----" << endl; v.push_back(boost::move(m2)); cout << "----- 3rd push_back -----" << endl; v.push_back(boost::move(m3)); cout << "----- 4th push_back -----" << endl; v.push_back(boost::move(m4)); cout << "----- vector ownership check -----" << endl; for (BVMOListIt it = v.begin(); it != v.end(); ++it) { if ((*it).hasOwnership()) { cout << (*it).value() << endl; } else { cout << "Lost Ownership" << endl; } } cout << "----- object ownership check -----" << endl; if (m1.hasOwnership()) { cout << m1.value() << endl; } else { cout << "Lost Ownership" << endl; } return 0; }
結果。
まず、このvectorはpush_backで既に「ムーブコンストラクタ」が呼ばれるので
所有権が「コンテナの中にあるMoveSemanticsObject」に移ります。
これは、m1, m2, m3, m4全てにおいて同様です。
そうして、push_backを続けていくと、
リアロケーションが起きるので
ここで新しい領域にvectorがコピーされますが、
ここでも「ムーブコンストラクタ」によって所有権が移動します。
念のためコピーコンストラクタが呼ばれないか監視していますが、呼ばれていません。
ここでもし「コピーコンストラクタ」が呼ばれた場合には、
通常のオブジェクトにおける「コピーコンストラクタ」は「複製しなければならない」に従い、
valueOwnership_を複製しなければなりません。
ここではたかだかintですが、
これでもnewとdeleteの回数が増加することは解ると思います。
このオブジェクトがもし膨大な情報を持っているならば「複製」することはとてもばかげたことかもしれません。
これが「ムーブセマンティクス」ならば単なるポインタのコピーで済みます。
こうしてコストを軽くすることが
「移動」と「コピー」を使い分けを意識できる「ムーブセマンティクス(move semantics)」の利点です。
では、次は右辺値参照(==BOOST_RV_REF==rvalue reference)について調べます。
(続く)
MoveSemanticsObject::MoveSemanticsObject() MoveSemanticsObject::MoveSemanticsObject() MoveSemanticsObject::MoveSemanticsObject() MoveSemanticsObject::MoveSemanticsObject() ----- 1st push_back ----- MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject)) ----- 2nd push_back ----- MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject)) MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject)) MoveSemanticsObject::~MoveSemanticsObject(ownership:NO) ----- 3rd push_back ----- MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject)) MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject)) MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject)) MoveSemanticsObject::~MoveSemanticsObject(ownership:NO) MoveSemanticsObject::~MoveSemanticsObject(ownership:NO) ----- 4th push_back ----- MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject)) MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject)) MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject)) MoveSemanticsObject(BOOST_RV_REF(MoveSemanticsObject)) MoveSemanticsObject::~MoveSemanticsObject(ownership:NO) MoveSemanticsObject::~MoveSemanticsObject(ownership:NO) MoveSemanticsObject::~MoveSemanticsObject(ownership:NO) ----- vector ownership check ----- 100 101 102 103 ----- object ownership check ----- Lost Ownership MoveSemanticsObject::~MoveSemanticsObject(ownership:NO) MoveSemanticsObject::~MoveSemanticsObject(ownership:NO) MoveSemanticsObject::~MoveSemanticsObject(ownership:NO) MoveSemanticsObject::~MoveSemanticsObject(ownership:NO) MoveSemanticsObject::~MoveSemanticsObject(ownership:YES) MoveSemanticsObject::~MoveSemanticsObject(ownership:YES) MoveSemanticsObject::~MoveSemanticsObject(ownership:YES) MoveSemanticsObject::~MoveSemanticsObject(ownership:YES)