CPPの右辺値参照・ムーブセマンティスス(その2)
前回の投稿に引き続き、もう少し遊んでみる。この投稿の内容は、 stackoverflow.com を参考にした。僕自身、あまり理解できていない気がするので、間違っていたら教えてください。
今回もやはり例を挙げて説明したいので、以下のような状況を想定する。
#include <iostream> struct something { int a = 1; something() {} something(const something& s) { std::cout << "copy: " << this << "<-" << &s << std::endl; } something(something&& s) { std::cout << "move: " << this << "<-" << &s << std::endl; } ~something() { std::cout << "destructor: " << this << std::endl; } }; something getV() { something sth = something(); std::cout << "at getV: " << &sth << std::endl; return sth; } something& getL() { something sth = something(); std::cout << "at getL: " << &sth << std::endl; return sth; } something&& getR() { something sth = something(); std::cout << "at getR: " << &sth << std::endl; return std::move(sth); }
この時点でコンパイラは、
warning: reference to stack memory associated with local variable 'sth' returned [-Wreturn-stack-address] return sth; ^~~
と言ってくる。ローカル変数の参照を返り値にしてはいけないのは、よく言われること。
続いて、getV、getR関数を使って見ると、
something r = getR();
std::cout << "Main: " << &r << std::endl;
at getR: 0x7fff5e550820 destructor: 0x7fff5e550820 move: 0x7fff5e550898<-0x7fff5e550820 Main: 0x7fff5e550898 destructor: 0x7fff5e550898
デストラクタが走ってからムーブコンストラクタが呼ばれている。破棄されたオブジェクトを使っているから、これは本来、実行時エラーが出るべきところだろう。
something&& r = getR();
std::cout << "Main: " << &r << std::endl;
at getR: 0x7fff53841850 destructor: 0x7fff53841850 Main: 0x7fff53841850
これも同じような結果になった。ただ、右辺値参照でgetR()を受けているから、ムーブコンストラクタは呼ばれず。もちろん、これも実行時エラーが出るべきところ。
something&& r = getV();
std::cout << "Main: " << &r << std::endl;
at getV: 0x7fff52371888 Main: 0x7fff52371888 destructor: 0x7fff52371888
これはいい感じ。
something r = getV();
std::cout << "Main: " << &r << std::endl;
at getV: 0x7fff56630898 Main: 0x7fff56630898 destructor: 0x7fff56630898
なんと、これが上の結果と同じだった。ちなみに、このやり方が参考サイトでは推奨されていた。
最後の例と、その前の例の実行結果が同じなのは、コンパイラがNamed Return Value Optimization(NRVO/RVO)を行なっているからだと思うが、詳細はここでは扱わず、他のサイトに委ねる。これらの結果はもっと考察しがいがあるので、時間ができたら記事を更新するつもり。
後記
さて、今回の投稿ではムーブセマンティクスがなぜ「ムーブ」なのか扱おうと思っていたのだが、なぜか違う方向に流れてしまった。おかげで、記事を書き終わってから、冒頭部分を慌てて書き換える始末。次こそは「ムーブ」のわけを探りたいと思う。