7

Function passed as template argumentに関して、Ben Supnik が提供するコミュニティ wiki の回答では、インスタンス化された関数テンプレートのインライン化の問題について説明しています。

その答えには次のコードがあります:

template<typename OP>
int do_op(int a, int b, OP op)
{
  return op(a,b,);
}

int add(int a, b) { return a + b; }

int (* func_ptr)(int, int) = add;

int c = do_op(4,5,func_ptr);

答えはこれを言い続けます(関数テンプレートをインスタンス化する最終行に関してdo_op):

明らかにこれはインライン化されていません。

私の質問はこれです:これがインライン化されていないことが明らかなのはなぜですか?

4

4 に答える 4

8

彼が言っていること (私が思うに) は、add関数がインライン化されていないということです。言い換えれば、コンパイラは次のdo_opようにインライン化する可能性があります。

int c = func_ptr(4, 5);

addただし、次のようにインライン化することもありません。

int c = 4 + 5;

しかし、その単純な例では彼は間違っているかもしれません。

一般に、ポインターを介して関数を呼び出す場合、コンパイラーは (コンパイル時に) 呼び出す関数を認識できないため、関数をインライン化できません。例:

void f1() { ... }
void f2() { ... }

void callThroughPointer() {
    int i = arc4random_uniform(2);
    void (*f)() = i ? f2 : f1;
    f();
}

ここで、コンパイラは or を呼び出すかどうかを知ることができないためcallThroughPointer、 or のいずれかをインライン化する方法はありません。f1f2f1f2callThroughPointer

ただし、コンパイラがコンパイル時にどの関数が呼び出されるかを証明できる場合は、関数をインライン化することができます。例:

void f1() { ... }
void f2() { ... }

void callThroughPointer2() {
    int i = arc4random_uniform(2);
    void (*f)() = i ? f2 : f1;
    f = f1;
    f();
}

fここで、コンパイラはが常に であることを証明できるため、f1にインラインf1化できcallThroughPointer2ます。(インライン化するとは限りませんf1…)

同様に、投稿で引用した例では、コンパイラfunc_ptrは が常にaddの呼び出しにあることを証明できるためdo_op、 inline が許可されていますadd。(インライン化するとは限りませんadd…)

于 2012-12-02T23:11:45.943 に答える
3

関数ポインターを介して関数を呼び出す場合、コンパイラーが関数ポインターを介した呼び出しを回避する可能性はほとんどありません。コンパイラーが、関数ポインターが初期化されているものを知っていて、それを変更できないことを証明できる場合にのみ、関数ポインターを介した関数呼び出しを回避し、関数をインライン化することができます。引用された設定では、すなわち、

int (* func_ptr)(int, int) = add;

関数ポインターfunc_ptrは変更可能であり、コンパイラーはそれが決して変更されないことを保証しません。その結果、 への呼び出しをインライン化できない可能性がありますadd

コードのスニペットが実際に完全である場合、初期化中に何かが発生し、実際にはコンパイラーは がfunc_ptr含まれるように初期化されていることを知ることができますadd

于 2012-12-02T23:23:05.217 に答える
1

これがインライン化されていないことが明らかなのはなぜですか?

そうではありません。コンパイラがそのスニペットのすべてのコードをインライン化できなかった理由はありません。

于 2012-12-02T23:22:55.323 に答える