More C++ Idioms/所有権移動コンストラクタ(Move Constructor)
所有権移動コンストラクタ(Move Constructor)
[編集]意図
[編集]あるオブジェクトに保持されているリソースの所有権を別のオブジェクトに移す。
別名
[編集]- Colvin-Gibbons trick
動機
[編集]C++ でのオブジェクトには、いわゆる move semantics(代入やコピーによって、オブジェクトがコピーされるのでもなく、所有権を共有するのでもなく、所有権が移動するように動作する)を示すものがある。例えば std::auto_ptr がそうである。 以下のコードの auto_ptr b は、オブジェクト a の生成後、無効となる。
std::auto_ptr <int> b (new int (10));
std::auto_ptr <int> a (b);
明らかに、auto_ptr のコピーコンストラクタは実際にはコピーコンストラクタではなく、またそれゆえ、パラメータとして const 参照をとらない。オブジェクト b が const オブジェクトではないため、上記のコードでは問題にはならないが、一時オブジェクトが含まれる場合には問題になる。
ある関数が値によってオブジェクトを返し、その返されたオブジェクトが別のオブジェクトの構築に使用される場合、コンパイラは返されたオブジェクトに対して一時オブジェクトを生成する。これらの一時オブジェクトは本当に短命であり、左辺のオブジェクトが問題なく生成された後、その一時オブジェクトのデストラクタが呼び出される。一時オブジェクトは、非常に短期間のリソース所有者である。問題は、一時オブジェクトがデフォルトでは const オブジェクトであることにある。そのため、非 const 参照をパラメータにとるコンストラクタ呼び出しに使うことができない。ここで、所有権移動コンストラクタ(Move constructor)が使われる。
解法とサンプルコード
[編集]template <class T>
class MoveResource
{
private:
struct proxy
{
T * resource_;
};
T * resource_;
public:
MoveResource (MoveResource &m) throw () // 所有権移動コンストラクタ(Move constructor)
: resource_ (m.resource_)
{
m.resource_ = 0;
}
MoveResource (proxy p) throw () // 代理所有権移動コンストラクタ(The proxy move constructor)
: resource_(p.resource_)
{
}
MoveResource & operator = (MoveResource &m) throw ()
{
// コピーして交換(copy and swap)イディオム
MoveResource temp (m);
temp.swap (*this);
return *this;
}
MoveResource & operator = (proxy p) throw ()
{
// コピーして交換(copy and swap)イディオム
MoveResource temp (p);
temp.swap (*this);
return *this;
}
void swap (MoveResource &m) throw ()
{
std::swap (this->resource_, m.resource_);
}
operator proxy () throw () // ヘルパー変換関数
{
proxy p;
p.resource_ = this->resource_;
this->resource_ = 0;
return p;
}
};
少なくとも基本例外保証を守るため、これらの関数が例外を送出しないことが重要である。 関数の戻り値から MoveResource が構築されようとする時、コンパイラは正しい変換関数の呼び出し順をはじきだす。最初に、代理(proxy)オブジェクトへの変換演算子が呼び出され、そして代理所有権移動コンストラクタが呼び出される。その間、例外が送出されてはならない。さもなくば、リソースリークが発生するだろう。 C++09 言語規格で追加される機能である右辺値参照(rvalue references)により所有権移動コンストラクタ(Move Constructor)イディオムは必要なくなるだろう。
既知の利用
[編集]std::auto_ptr
関連するイディオム
[編集]- リソース獲得は初期化である(Resource Acquisition Is Initialization (RAII))
- スコープ防壁(Scope Guard)
- リソースの返値(Resource Return)