2016-11-18 5 views
3

メンバー関数ポインタがテンプレートパラメータとして使用されているときに何が起こっているのかを理解しようとしています。私はいつも関数ポインタ(またはメンバ関数ポインタ)は実行時のコンセプトだと考えていたので、テンプレートパラメータとして使用するとどうなるのだろうかと思っていました。 callFは、比較のためにあるなぜgccとclangはメンバ関数のテンプレートパラメータに非常に異なるコードを生成するのですか?

struct Foo { void foo(int i){ } };  
template <typename T,void (T::*F)(int)> 
void callFunc(T& t){ (t.*F)(1); } 
void callF(Foo& f){ f.foo(1);}  
int main(){ 
    Foo f; 
    callF(f); 
    callFunc<Foo,&Foo::foo>(f); 
} 

:このような理由から、私はこのコードによって生成された出力を見ていました。

callF(Foo&):       # @callF(Foo&) 
    push rbp 
    mov  rbp, rsp 
    sub  rsp, 16 
    mov  esi, 1 
    mov  qword ptr [rbp - 8], rdi 
    mov  rdi, qword ptr [rbp - 8] 
    call Foo::foo(int) 
    add  rsp, 16 
    pop  rbp 
    ret 

が、テンプレートのインスタンス化のために非常に異なる出力:clang 3.9callF()でほぼ同じ出力を生成しながら

callF(Foo&): // void callFunc<Foo, &Foo::foo>(Foo&): 
    push rbp 
    mov  rbp, rsp 
    sub  rsp, 16 
    mov  QWORD PTR [rbp-8], rdi 
    mov  rax, QWORD PTR [rbp-8] 
    mov  esi, 1 
    mov  rdi, rax 
    call Foo::foo(int) 
    nop 
    leave 
    ret 

void callFunc<Foo, &Foo::foo>(Foo&): # @void callFunc<Foo, &Foo::foo>(Foo&) 
    push rbp 
    mov  rbp, rsp 
    sub  rsp, 32 
    xor  eax, eax 
    mov  cl, al 
    mov  qword ptr [rbp - 8], rdi 
    mov  rdi, qword ptr [rbp - 8] 
    test cl, 1 
    mov  qword ptr [rbp - 16], rdi # 8-byte Spill 
    jne  .LBB3_1 
    jmp  .LBB3_2 
.LBB3_1: 
    movabs rax, Foo::foo(int) 
    sub  rax, 1 
    mov  rcx, qword ptr [rbp - 16] # 8-byte Reload 
    mov  rdx, qword ptr [rcx] 
    mov  rax, qword ptr [rdx + rax] 
    mov  qword ptr [rbp - 24], rax # 8-byte Spill 
    jmp  .LBB3_3 
.LBB3_2: 
    movabs rax, Foo::foo(int) 
    mov  qword ptr [rbp - 24], rax # 8-byte Spill 
    jmp  .LBB3_3 
.LBB3_3: 
    mov  rax, qword ptr [rbp - 24] # 8-byte Reload 
    mov  esi, 1 
    mov  rdi, qword ptr [rbp - 16] # 8-byte Reload 
    call rax 
    add  rsp, 32 
    pop  rbp 
    ret 

gcc 6.2は、両方の機能のために正確に同じ出力を生成します何故ですか? gccは(おそらく非標準の)ショートカットをとっていますか?

+0

「-O3」?どのようなコンパイラ設定、まさに? – Yakk

+0

@ Yakkよく、私はそれが最適化なしであることを認めなければなりません。私は観察可能な結果を​​生成する例を出す努力をしませんでした。 – user463035818

答えて

5

gccはテンプレートが何をしているのか把握することができ、可能な限り単純なコードを生成しました。 clangはしませんでした。コンパイラは、観察可能な結果がC++仕様に準拠している限り、最適化を実行することができます。中間の関数ポインタを最適化する場合は、そうするようにしてください。コード内の他のものはテンポラリ関数ポインタを参照しないので、完全に離れて最適化することができ、全体が単純な関数呼び出しに置き換えられます。

gccとclangは、異なる人々によって書かれた異なるコンパイラであり、C++をコンパイルするためのさまざまなアプローチとアルゴリズムを備えています。

これは当然であり、異なるコンパイラの結果が異なることが予想されます。この場合、gccはclangよりも良い結果を得ることができました。私はclangがgccより良いことを理解することができる他の状況があると確信しています。

+0

ちょっと混乱している唯一の詳細は、 .. "なぜ一時的なのですか?それは本当に一時的な関数ポインタですか?私は、テンプレートのパラメータはコンパイル時に評価される、いいえ? – user463035818

+0

関数ptrはテンプレート関数を生成します。文字通りコンパイルされると、生成された関数が呼び出されることを表す、ある種の記号が表示されます。 –

3

このテストは、最適化が要求されずに行われました。

1つのコンパイラが、より詳細な最適化されていないコードを生成しました。

最適化されていないコードは、非常に単純ですが、面白くありません。これは、最適化が容易ないくつかの中間表現から直接的にデバッグし、直接導き出すことを目的としています。

最適化されたコードの詳細は、デバッグを苦痛にさせるばかばかしい広範な減速を避けて、何が重要です。

ここで見ることや興味深いことは何もありません。

+1

"ここで見るか説明するのは興味深いことは何もありません。"失礼だが真実だ)私はこの問題を削除し、最適化を有効にして見る時間があるときに問題に戻ってくるだろうと思う。 – user463035818

+0

@tobi私はそれを鈍いと呼ぶのが好きだ;) – Yakk

関連する問題