2016-07-05 6 views
1

は、以下のプログラムを検討値を返すことができます理由:「値」であると思われるので、関数fにおける変数「RET」(のC++での関数はスタックが

class C { 
    ... 
}; 

const C f() { 
    C ret; 
    cout << &ret << endl; 
    return ret; 
} 

int main() { 
    C value = f(); 
    cout << &value << endl; 
} 


result: // note the address are the same 
0x7ffdd24b26e0 
0x7ffdd24b26e0 

を)し、変数「値は」同じメモリアドレスを持っています'ret'のコピーではありません。変数 'ret'はスタック変数なので、f()が復帰した後に無効にする必要があります。なぜ関数内でスタック値を返すことができるのですか?

グラム++バージョン:

g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4 
+0

最初の投稿または2番目の投稿のいずれかで移動する2番目の質問に関するメモを追加する必要はありません。それは余分なノイズです。 –

答えて

4

RETと値が同じであることのアドレスのための理由は、いわゆる戻り値の最適化(RVO)です。この場合、コピーは実行されないことを意味します。ただし、これに頼ることはできません(これはC++ 17(少なくとも現在のドラフト)で変更されますが)。

https://en.wikipedia.org/wiki/Return_value_optimization

+0

RVOはそれをとてもうまく説明します。ありがとう。 – darklord

+0

ニックピッキングしているかもしれませんが、実際はNRVO(** Named ** Return Value Optimization)のケースです。 – MikeMB

3

これは、コピーエリジオン、具体RVO(戻り値の最適化)の一例です。これにより、オブジェクトを返す際のパフォーマンスの低下を回避できます。

私はRVOの知らなかった多くの人々と協力してのようなものを書いてきました:

void get_stuff(/*input params*/){ 
     std::vector foo; 
     // populate foo. 
     return foo; 
} 
:彼らはこれよりも(コピーを回避することによって)安くなるだろうと思ったので

void get_stuff(std::vector<int>& foo /*and input params*/) { 
    // add a whole lot of integers into foo. 
} 

これは不必要に冗長で、コードを読むのが難しいことがあります。あなたはRVOについて今知っているので、それは本質的な早すぎる最適化です - 間違いはありません!値によって関数の結果を返す

+0

そして知っていることは戦闘の半分です。 Yo JOE! – user4581301

1

は、コンパイラが最適化としてコピーをElideのために許可されている場所の一つ、であるため、これの道徳的同等にコードを変換します。

void f(uint8_t* memory) { 
    new(memory) C; // create object reserved memory location 
    cout << (size_t)memory << endl; 
} 

int main() { 
    alignas(alignof(C)) uint8_t value[sizeof(C)]; //<- reserve properly aligned raw storage of appropriate size on the stack 
    f(value); 
    cout << (size_t)&value[0] << endl; 
} 

この最適化手法NRVO(戻り値の最適化)と呼ばれ、実際にはレジスタを介して返すことができない値については、呼び出し元によって指定されたアドレスに戻り値が置かれることを指定するほとんどの呼び出し規約の自然な帰結ですとにかく

0

これが実際にRVOかどうかはわかりません。同様に有効な仮説は、retvalueの両方のストレージが実装のスタック上にあり、それらのライフタイムが効果的にオーバーラップしないため同じスタックスロットを共有するということです。仲介者がいた可能性があります。

関連する問題