以下は、C++ 標準でコンパイラがコードを再構築できるものの例です。フルNRVOを使用しています。new
ややあいまいな C++ 機能であるplacement の使用に注意してください。ポインターを渡すnew
と、フリー ストアではなく、そこに結果が構築されます。
#include <iostream>
void __foo(void* __construct_std_string_at)
{
new(__construct_std_string_at)std::string("abc");
}
void __goo(void* __construct_std_string_at)
{
__foo(__construct_std_string_at);
}
int main()
{
unsigned char __buff[sizeof(std::string)];
// Is a temporary allocated on the heap to support this, even for a moment?
__goo(&__buff[0]);
const std::string & b = *reinterpret_cast<std::string*>(&__buff[0]);
// ... more code here using b I assume
// end of scope destructor:
reinterpret_cast<std::string*>(&__buff[0])->~std::string();
}
で NRVO をブロックすると、次goo
のようになります。
#include <iostream>
void __foo(void* __construct_std_string_at)
{
new(__construct_std_string_at)std::string("abc");
}
void __goo(void* __construct_std_string_at)
{
unsigned char __buff[sizeof(std::string)];
__foo(&__buff[0]);
std::string & a = *reinterpret_cast<std::string*>(&__buff[0]);
new(__construct_std_string_at)std::string(a);
// end of scope destructor:
reinterpret_cast<std::string*>(&__buff[0])->~std::string();
}
int main()
{
unsigned char __buff[sizeof(std::string)];
// Is a temporary allocated on the heap to support this, even for a moment?
__goo(&__buff[0]);
const std::string & b = *reinterpret_cast<std::string*>(&__buff[0]);
// ... more code here using b I assume
// end of scope destructor:
reinterpret_cast<std::string*>(&__buff[0])->~std::string();
}
基本的に、コンパイラは参照の有効期間を認識しています。したがって、変数の実際のインスタンスを格納する「匿名変数」を作成し、それへの参照を作成できます。
また、関数を呼び出すときに、戻り値が格納されるバッファーへのポインターを効果的に (暗黙的に) 渡すことにも注意しました。したがって、呼び出された関数は、呼び出し元のスコープ内で「その場で」オブジェクトを構築します。
NRVO を使用すると、呼び出された関数スコープ内の名前付き変数は、実際には呼び出し関数内で「戻り値が移動する場所」に構築されるため、簡単に戻ることができます。それがなければ、すべてをローカルで行う必要があります。その後、return ステートメントで、placement new と同等のものを使用して、戻り値を戻り値への暗黙のポインターにコピーします。
ライフタイムはすべて簡単に証明可能であり、スタック順に並べ替えられるため、ヒープ (別名フリー ストア) で何もする必要はありません。
元の署名foo
とgoo
予想される署名は、外部リンクがあるため、誰も使用していないことが判明したときに破棄されるまで、まだ存在している必要があります。
で始まるすべての変数と関数__
は、説明のためだけに存在します。コンパイラ/実行環境には、赤血球に名前を付ける必要があるのと同様に、名前付き変数を含める必要はありません。(理論的には、__
は予約済みであるため、コンパイル前にそのような変換パスを実行したコンパイラはおそらく合法であり、実際にそれらの変数名を使用してコンパイルに失敗した場合、それはコンパイラのせいではなくあなたのせいになりますが...それはかなりハッキーなコンパイラです。;) )