回答:
まず、inline
関数の仕様はヒントにすぎません。コンパイラは、inline
修飾子の有無を完全に無視できます(多くの場合、無視します)。そうは言っても、コンパイラは無限ループを展開できるのと同じように、再帰関数をインライン化できます。関数を「アンロール」するレベルに制限を設けるだけです。
最適化コンパイラはこのコードを有効にするかもしれません:
inline int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int f(int x)
{
return factorial(x);
}
このコードに:
int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int f(int x)
{
if (x <= 1)
{
return 1;
}
else
{
int x2 = x - 1;
if (x2 <= 1)
{
return x * 1;
}
else
{
int x3 = x2 - 1;
if (x3 <= 1)
{
return x * x2 * 1;
}
else
{
return x * x2 * x3 * factorial(x3 - 1);
}
}
}
}
この場合、基本的には関数を3回インライン化しました。一部のコンパイラはこの最適化を実行します。MSVC ++には、再帰関数で実行されるインライン化のレベルを調整する設定があることを思い出します(最大20と思います)。
実際、コンパイラーがインテリジェントに動作しない場合、inline
d関数のコピーを再帰的に挿入して、無限に大きなコードを作成する可能性があります。ただし、最近のほとんどのコンパイラはこれを認識します。次のいずれかを行うことができます。
ケース2の場合、多くのコンパイラーには#pragma
、これを実行する最大深度を指定するために設定できるsがあります。ではGCC、あなたもでコマンドラインからでこれを渡すことができます--max-inline-insns-recursive
(詳細を参照してくださいここに)。
一部の再帰関数はループに変換でき、無限にインライン化されます。gccでこれができると思いますが、他のコンパイラについては知りません。
これが通常は機能しない理由については、すでに与えられた回答を参照してください。
「脚注」として、テンプレートメタプログラミングを使用して、(少なくとも例として使用している階乗に対して)探している効果を得ることができます。ウィキペディアからの貼り付け:
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
「コンパイラーは関数をインライン化するかどうかをどのように決定するのですか?」
これは、コンパイラ、指定されたオプション、コンパイラのバージョン番号、使用可能なメモリ容量などによって異なります。
プログラムのソースコードは、インライン関数のルールに従う必要があります。関数がインライン化されるかどうかに関係なく、インライン化される可能性に備えて準備する必要があります(回数は不明です)。
再帰マクロは通常違法であるというウィキペディアの声明は、情報が不十分であるように見えます。CおよびC ++は再帰的な呼び出しを防止しますが、再帰的であるように見えるマクロコードを含めることにより、翻訳単位が違法になることはありません。アセンブラでは、通常、再帰マクロは有効です。