C++0xのムーブセマンティクス(値のつなぎ替え)と右辺値参照
Faith and Braveさんの記事を参考に、C++Builder 2009で試してみた。
//--------------------------------------------------------------------------- #include <iostream> #include <utility> #pragma hdrstop #include <tchar.h> //--------------------------------------------------------------------------- class CFoo { public: CFoo() : m_Value(0) { } CFoo(const CFoo& foo) : m_Value(foo.m_Value) { std::cout << "Copy constructor" << std::endl; } CFoo(CFoo&& foo) : m_Value(foo.m_Value) { std::cout << "Move constructor" << std::endl; foo.m_Value = 0; } CFoo& operator=(CFoo&& foo) { std::cout << "operator=(Move)" << std::endl; std::swap(m_Value, foo.m_Value); return *this; } public: int m_Value; }; CFoo FooFunc() { CFoo foo; foo.m_Value = 2000; return foo; } #pragma argsused int _tmain(int argc, _TCHAR* argv[]) { CFoo foo1; foo1.m_Value = 1000; // 値のコピー std::cout << "CFoo lfoo = foo1;" << std::endl; CFoo lfoo = foo1; std::cout << "foo1.m_Value = " << foo1.m_Value << std::endl << "lfoo.m_Value = " << lfoo.m_Value << std::endl << std::endl; // 値の転送(転送元は破壊される) std::cout << "CFoo rfoo1 = std::move(foo1);" << std::endl; CFoo rfoo1 = std::move(foo1); std::cout << "foo1.m_Value = " << foo1.m_Value << std::endl << "rfoo1.m_Value = " << rfoo1.m_Value << std::endl << std::endl; // 戻り値(一時オブジェクト)の参照(1) std::cout << "CFoo&& rfoo2 = FooFunc();" << std::endl; CFoo&& rfoo2 = FooFunc(); std::cout << "rfoo2.m_Value = " << rfoo2.m_Value << std::endl << std::endl; // 戻り値(一時オブジェクト)の参照(2) std::cout << "CFoo rfoo3 = FooFunc();" << std::endl; CFoo rfoo3 = FooFunc(); std::cout << "rfoo3.m_Value = " << rfoo3.m_Value << std::endl; return 0; } //---------------------------------------------------------------------------
こいつをコンパイルしてみる。
D:\home\A7M\BCBTEST\rvalue_ref>bcc32 File1.cpp CodeGear C++ 6.10 for Win32 Copyright (c) 1993-2008 CodeGear File1.cpp: Turbo Incremental Link 5.96 Copyright (c) 1997-2008 CodeGear D:\home\A7M\BCBTEST\rvalue_ref>file1 CFoo lfoo = foo1; Copy constructor foo1.m_Value = 1000 lfoo.m_Value = 1000 CFoo rfoo1 = std::move(foo1); Move constructor foo1.m_Value = 0 rfoo1.m_Value = 1000 CFoo&& rfoo2 = FooFunc(); Move constructor rfoo2.m_Value = 2000 CFoo rfoo3 = FooFunc(); Move constructor rfoo3.m_Value = 2000 D:\home\A7M\BCBTEST\rvalue_ref>
「ムーブセマンティクス」とは、関数の戻り値のような一時オブジェクトは破棄されることがあらかじめ判っているから、ならば、その一時オブジェクトが握っているリソースを受け側のリソースとして「つなぎ替える」ことでリソースを複写をせずに、受け手へ高速に値を渡す手法。「右辺値参照」と「ムーブコンストラクタ」はムーブセマンティクスを実装する為にC++0xで導入された。「右辺値」とは、関数の戻り値のように変数名とは結び付いていない(=代入出来ない)オブジェクト。*1「右辺値参照」とは、値を「右辺値」として参照する。今までの「参照」は狭義の「左辺値参照」となる。*2「ムーブコンストラクタ」は右辺値が渡されたときのコンストラクタと理解していいのかな?ムーブコンストラクタとコピーコンストラクタの両方がある場合、ムーブコンストラクタが優先される。
やりたいことは何となく理解したつもりだけど、簡潔に説明するのは難しそうだ。それに、CFoo::operator=(CFoo&& foo)が呼び出されるシチュエーションがよく判らないけど、派生クラスとかのムーブコンストラクタ内で呼ばれる場合があるのかな?