定数を使用した関数呼び出しがある場合、副作用はなく、次のように何にも依存しません。
int foo(int a,int b) { a+b を返します。 }
関数はインライン化されますか? それとも、コンパイル時に関数が評価され、この評価の結果が関数呼び出しの代わりに挿入されるのでしょうか?
定数を使用した関数呼び出しがある場合、副作用はなく、次のように何にも依存しません。
int foo(int a,int b) { a+b を返します。 }
関数はインライン化されますか? それとも、コンパイル時に関数が評価され、この評価の結果が関数呼び出しの代わりに挿入されるのでしょうか?
かなり古いgccを使用してこれをコンパイルしようとしました-
#include <iostream>
int foo(int a,int b)
{
return a+b;
}
int main()
{
std::cout << foo(100, 123) ;
}
そしてこれにコンパイルされたメイン -
LFB1439:
subq $8, %rsp
.LCFI1:
movl $223, %esi
movl $_ZSt4cout, %edi
call _ZNSolsEi
xorl %eax, %eax
addq $8, %rsp
ret
したがって、コンパイル時に223を取得して追加をコンパイルしました。
明らかに、結果はコードとコンパイラに依存しますが、これは、可能であればコンパイル時にインラインと計算の両方を実行できることを示しています。
C++ にはありません。コンパイラが魔法のように実行しない限り、それらはコンパイル時に実行されません。ただし、これを強制することはできません。
constexpr
ただし、C++11 では、コンパイル時に評価されるようにするために使用できます。
constexpr int get_five() {return 5;}
したがって、関数を次のように書き換えることができます。
constexpr int foo(int a,int b)
{
return a+b;
}
この関数の引数が常に一定でなくても心配する必要はありません。
ウィキペディアから:
constexpr 関数またはコンストラクターが定数式ではない引数で呼び出された場合、呼び出しは関数が constexpr ではないかのように動作し、結果の値は定数式ではありません。同様に、constexpr 関数の return ステートメントの式が特定の呼び出しの定数式に評価されない場合、結果は定数式ではありません。
これはfoo(1,1)
一定であることを意味しますが、
int i,j;
cin >> i >> j;
foo(i,j) // this is not constant
参照: http://en.wikipedia.org/wiki/C%2B%2B11#constexpr_-_Generalized_constant_expressions
ヘッダー ファイルでそれを定義すると、インライン化される可能性が高くなります。整数のコンパイル時定数を引数として使用している場合、コンパイラはコンパイル時にその関数を実行できるはずです。
そのような保証はありませんが、コンパイラを信頼する必要があります。コードの最適化が得意です。関数がコンパイル時に確実に実行されるようにする場合は、constexpr
オーバーロードを追加できます (C++11 のみ)。
constexpr int foo(int a,int b){
return a+b;
}
次のスニペットを試しました:
int add(int a, int b) {
return a + b;
}
int main() {
return add(5, 2);
}
GCC と -O3 フラグを使用してコンパイルすると、次のようにコンパイルされます。
0x08048300 <+0>: mov $0x7,%eax
0x08048305 <+5>: ret
したがって、コンパイル時に実際に実行されていることがわかります。
このような最適化が実行されるかどうかは、C および C++ 言語の定義された部分ではありません。基本的に、結果のコードがソースに従って有効である限り、コンパイラは自由に最適化できます。一般に、より高い最適化レベルでは、この呼び出しをインライン化するか、呼び出しサイトが常に定数 (コンパイル時に既知の値) を渡す場合、コンパイル時に結果を計算でき、実行時のオーバーヘッドを完全に回避できます。
最適化コンパイラが関数をインライン化しないことを選択する一般的なケースは次のとおりです。
注意すべきもう 1 つの問題は、インライン化によって関数のリンケージが変更されることです。
を使用して、GCC と G++ の両方で次の C コードをコンパイルします-O3
。
int foo(int a, int b) {
return a+b;
}
int main(void)
{
return foo(1, 2);
}
次のアセンブリ コードが生成されます。
00000000004004e0 <main>:
main():
4004e0: b8 03 00 00 00 mov $0x3,%eax
4004e5: c3 retq
コンパイル時にこの関数を計算する方法として考えられるシナリオ: 1)インライン化最適化フェーズの 1 つでの
コンパイラのインライン関数。
2)定数伝播 の最適化フェーズ中に、コンパイラはコンパイル時に既知の変数の値、つまり定数と定数式を「伝播」できます。foo
注意: プログラムのアセンブラ コードを見るまで、関数がインライン化されているかどうかは正確にはわかりません。inline
指定子を使用しても。コンパイラは、この指定子またはこの指定子のないインライン関数を無視する場合があります。
アセンブリ リストをチェックして、インライン化されているかどうかを確認できますが、前述のように、これはコンパイラ固有のものです。
これはコンパイラと最適化設定に依存しますが、一般に、十分に高度なコンパイラは、少なくとも少しの最適化をオンにすると、そのような単純な関数をインライン化すると想定できます。
関数がインライン化されていることを確認したい場合は、いつでも inline キーワードで宣言できます。
inline int foo(int a,int b){
return a+b;
}
ただし、ほとんどのコンパイラは、ほとんどのプログラマよりもインライン化する関数の決定に優れているため、このような意図的なヒントは一般的に避ける必要があります。