まず第一に、C でプログラミングしているわけではありません。C++ でプログラミングしています。それは別の言語です。C には参照もクラスもありません。コードではクラスを使用していませんが、参照を使用しています (理由はありません)。
次に、「関数ポインタ」の意味を考えてみましょう。その関数のコードのアドレスです。ここで、「インライン」の意味を考えてみましょう。これは、関数がマシンコードに独立して存在しないことを意味します。そのソースは (概念的に) 呼び出された場所に配置され、呼び出し元の関数がコンパイルされます。したがって、存在しないもののアドレスを取得する必要があります。
短い答えは、あなたができないということです。
何を達成したいですか?常に b から foo を呼び出しますか? もしそうなら、間接化は必要ありません。インライン関数を呼び出して呼び出しサイトにインライン化することも、インライン化されていない関数を呼び出して実際の関数呼び出しを生成することもできます。
// a.c
double foo(int a) { return a * 3.1415926; }
// b.c
double foo(int a); // declaration, not definition.
void b(void) { printf("%f\n", foo(10)); }
int main() { b(); return 0; }
ビルド:
gcc -O2 -c -o a.o a.c
bc をコンパイルするとき、コンパイラは関数 foo が何をするかを知らず、そのシグネチャしか知らないことに注意してください。
gcc -O2 -c -o b.o b.c
これにより、2 つの部分が 1 つのプログラムにリンクされ、実行できるようにする部分が最初と最後に追加されます。
gcc -o program a.o b.o
違いは効率です。インライン化により、多くの最適化が可能になります (ここのように、引数の値を使用して結果を事前計算し、コードを完全に削除するまで)。このような最適化が不可能なインライン化は、関数呼び出しのオーバーヘッドを排除し、より適切なレジスタ割り当てを可能にするだけでなく、コードがキャッシュ内にある可能性を高めることができるため、高速化することができます。しかし、インライン化は、コードの肥大化とキャッシュの汚染につながり、コードがキャッシュに存在する可能性が低くなるため、悪い場合があります。
コンパイル時に b から呼び出したい関数がわからない場合は、間接化が必要です。したがって、foo_1 と foo_2 があり、どちらかへのポインターを b に渡すと、そのポインターが指す関数がどちらであるかを知らずに呼び出されます。これにはパフォーマンス上のペナルティがありますが、場合によっては必要になります。
double foo_1(int a) { return a * 3.1415926; }
double foo_2(int a) { return a * 2.81; }
void b(double (*foo)(int)) { printf("%f\n", foo(10)); }
int main(int argc, char *argv[])
{
if (argc < 2) return;
b(argv[1][0]-48 ? &foo_1 : &foo_2);
return 0;
}
C++ には、同じソース コードから関数 b の 2 つのバージョン (常に foo_1 を呼び出すものと常に foo_2 を呼び出すもの) を生成するようにコンパイラに指示できる機能があります。次に、foo_1 と foo_2 を両方の呼び出しサイトでインライン化することができ、foo_1 または foo_2 へのポインターを b に渡すことを選択する代わりに、b_with_foo_1 または b_with_foo_2 を呼び出すことを選択する必要があります。