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)を行なっているからだと思うが、詳細はここでは扱わず、他のサイトに委ねる。これらの結果はもっと考察しがいがあるので、時間ができたら記事を更新するつもり。

後記

さて、今回の投稿ではムーブセマンティクスがなぜ「ムーブ」なのか扱おうと思っていたのだが、なぜか違う方向に流れてしまった。おかげで、記事を書き終わってから、冒頭部分を慌てて書き換える始末。次こそは「ムーブ」のわけを探りたいと思う。