3

この単純なスニペットをコンパイルしました。

#include <iostream>
#include <string>

std::string foo()
{
    return std::string("bar");
}

int main()
{
    std::string test = foo();
    std::cout << test << std::endl;
    return 0;
}

最適化を使用し-O2て、2つのstd::stringオブジェクトが作成されていることを検出するだけです。バイナリをダンプすると、objdumpはそれ~basic_stringが2回呼び出されたことを示します。

0000000000400900 <main>:
  400900:   53                      push   %rbx
  400901:   48 83 ec 10             sub    $0x10,%rsp
  400905:   48 89 e7                mov    %rsp,%rdi
  400908:   e8 73 01 00 00          callq  400a80 <foo()>
  40090d:   48 89 e6                mov    %rsp,%rsi
  400910:   bf 80 10 60 00          mov    $0x601080,%edi
  400915:   e8 a6 ff ff ff          callq  4008c0 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@plt>
  40091a:   48 89 c7                mov    %rax,%rdi
  40091d:   e8 ae ff ff ff          callq  4008d0 <std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)@plt>
  400922:   48 89 e7                mov    %rsp,%rdi
  400925:   e8 76 ff ff ff          callq  4008a0 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()@plt>
  40092a:   48 83 c4 10             add    $0x10,%rsp
  40092e:   31 c0                   xor    %eax,%eax
  400930:   5b                      pop    %rbx
  400931:   c3                      retq   
  400932:   48 89 c3                mov    %rax,%rbx
  400935:   48 89 e7                mov    %rsp,%rdi
  400938:   e8 63 ff ff ff          callq  4008a0 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()@plt>
  40093d:   48 89 df                mov    %rbx,%rdi
  400940:   e8 ab ff ff ff          callq  4008f0 <_Unwind_Resume@plt>
  400945:   66 66 2e 0f 1f 84 00    data32 nopw %cs:0x0(%rax,%rax,1)
  40094c:   00 00 00 00 

本当に必要なオブジェクトは1つだけなのでfoo()、右辺値参照を使用して返される値をキャプチャすることを考えました。そのため、コード行をstd::string && test = foo();Weirdlyに変更しましたが、objdumpにはまだ呼び出された2つのデストラクタが表示されます。誰かが私に理由を説明できますか?

4

1 に答える 1

9

最初のデストラクタ呼び出しの後に、いくつかの指示が続きますretq

  400925:   e8 76 ff ff ff          callq  4008a0 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()@plt>
  ...
  400931:   c3                      retq   

これは通常のコードフローです。

後続のmovatから開始するの400932は、例外の伝播を伴うスタックの巻き戻しに内部的に使用されるコードであり、多くの場合、ランディングパッドと呼ばれます。

  400932:   48 89 c3                mov    %rax,%rbx
  400935:   48 89 e7                mov    %rsp,%rdi
  400938:   e8 63 ff ff ff          callq  4008a0 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()@plt>
  40093d:   48 89 df                mov    %rbx,%rdi
  400940:   e8 ab ff ff ff          callq  4008f0 <_Unwind_Resume@plt>

GCCが言っていることは次のとおりです

  • 例外処理の着陸パッドを生成する

    このパスは、関数内の例外処理ライブラリルーチンと例外ハンドラーの間の通信を処理する接着剤を生成します。例外処理ライブラリによって呼び出される関数のエントリポイントは、ランディングパッドと呼ばれます。このパスのコードは内にありますexcept.c

ご覧のとおり、制御フローのパスは完全に異なるため、デストラクタはどちらの方法でも1回だけ呼び出されます。

_Unwind_Resumeは、例外タイプをキャッチできる関数に到達するまでコールスタックを巻き戻す手段として、AMD64およびItanium C++ABIの一部です。あなたはグーグルからそれに関する多くの情報を見つけるために少し掘り下げる必要があるでしょう。これはそれを議論するかなり良いリソースです。

_Unwind_Resume _

void _Unwind_Resume
(struct _Unwind_Exception *exception_object);

部分的に巻き戻されたスタックでクリーンアップコードを実行した後など、既存の例外の伝播を再開します。このルーチンの呼び出しは、クリーンアップを実行したが通常の実行を再開しなかったランディングパッドの最後に挿入されます。巻き戻しがさらに進みます。

_Unwind_Resume再スローを実装するために使用しないでください。アンワインドランタイムでは、再スローするキャッチコードはハンドラーであり、前のアンワインドセッションは開始する前に終了しました。_Unwind_RaiseException再スローは、同じ例外オブジェクトを使用して再度呼び出すことで実装されます。

これは、生成されたコードによって直接呼び出されることが期待されるアンワインドライブラリ内の唯一のルーチンです。「ランディングパッド」モデルのランディングパッドの最後で呼び出されます。

于 2012-08-29T01:38:57.997 に答える