インライン関数
インライン関数(英: inline function)とは、プログラミング言語の言語機能および構文の一種で、コンパイラに対して特定の関数をインライン展開するよう指示するものである。つまり、コンパイラに対して、その関数を呼び出している全ての箇所に関数の実体を挿入するよう指示する。
意義
[編集]インライン展開は、関数呼び出しにかかるオーバーヘッドを無くす目的で行われる。一般に関数の処理内容自体が非常に小さくオーバーヘッドの割合が無視できない場合に使われる。非常に小さい関数の場合、メモリ使用量を削減することもでき、各種最適化を施す上でも有利である。
インライン関数がない場合、プログラマはどの関数をインライン展開すべきかを指定できず、コンパイラが勝手に判断することになる。インライン関数機能があれば、そのアプリケーション固有の知識を元にして(どの関数がよく使われるかなど)、インライン展開すべき関数を決定できる。
また、言語によってはインライン関数はコンパイルモデルと密接に関連している。例えばC++では、インライン関数はモジュール単位に定義する必要がある(通常の関数は1つのモジュールで定義すればよい)。これにより、モジュール単位に独立したコンパイルができるようになっている。
マクロとの比較
[編集]古いC言語(C89)などでは、インライン展開をソースレベルの引数付きマクロで実現してきた。インライン関数はマクロに比べて次のような利点がある。
- マクロ呼び出しは型チェックをしない。また、引数が正しい形式であるかもチェックしない。インライン関数呼び出しではこれらがチェックされる。
- C言語のマクロは単なる文字列の置換であり、予期せぬ副作用や、引数の評価を複数回行ってしまうことによる弊害が生じることがある。インライン関数はそのような副作用をもたらさない。
- マクロ内部でのコンパイルエラーは、マクロ展開後のコードで発生するため、プログラマにとっては理解が難しくデバッグに時間がかかることがある。インライン関数はコンパイラが処理するため、コンパイルエラーの発生個所および原因の特定もしやすい。
- マクロ内では構文が制限され、通常とは異なった書き方を要求される。インライン関数は通常の関数と全く同じであり、インライン化するかどうかも自由に決定できる。
- インライン化されたコードのデバッグ情報はマクロを展開したコードよりも扱いやすい。ブレークポイントの作成も可能である。
- 多くのコンパイラではある種の再帰呼び出し関数もインライン展開できる。再帰的マクロは一般に不正である。
これらの利点は、マクロを使用したジェネリックプログラミングに対する、C++の関数テンプレートのメリットと同様である。
C++の設計者ビャーネ・ストロヴストルップは、マクロよりもインライン関数を使うべきだと主張している。
言語サポート
[編集]C++およびC99以降のC言語は inline
キーワードによるインライン関数を標準サポートしている。Microsoft Visual C++はバージョン2017においてもC99をフルサポートしておらず、C言語モードでは inline
キーワードをサポートしていないが、代わりにインライン関数は独自拡張の __inline
キーワードで対応している[1][2][注釈 1]。Microsoft Visual C++やg++などは、インライン関数として指定されていなくてもインライン展開すべき関数を自動的に展開するオプションを用意している。Adaでは pragma
をインライン関数として使うことができる。Delphi (Object Pascal) はバージョン2005以降でインライン関数のサポートを追加した。JavaやJavaScriptは言語仕様には存在しないが、JITコンパイラが最適化の一環でインライン展開することもある。OracleのJava SEコンパイラはインラインディレクティブオプションをサポートする[3]。関数型言語など他の多くの言語はインライン関数をサポートしていないが、インライン展開を積極的に行うことが多い。インライン展開の方針はコンパイラによって異なる。
C99/C++でのインライン関数の定義例および使用例を以下に示す。
inline int max(int a, int b) {
if (a > b)
return a;
else
return b;
}
...
int x = 1, y = -2;
int z = max(x--, y); // 1
C++ではクラスおよび構造体のインラインメンバー関数を定義することもできる。クラスおよび構造体の型定義内に直接実装を記述したメンバー関数は、暗黙的にinline
となる[4]。
class MyClass {
int m_number1;
int m_number2;
public:
explicit MyClass(int number1, int number2) : m_number1(number1), m_number2(number2) {}
inline int getNumber1() const { return this->m_number1; }
int getNumber2() const { return this->m_number2; } // 暗黙的に inline となる。
int getSum() const;
};
inline int MyClass::getSum() const { return this->m_number1 + this->m_number2; }
C++では、関数テンプレート、コンパイラが暗黙的に宣言と定義を自動生成した特殊メンバー関数[注釈 2]、C++11以降のconstexpr
関数[7]も暗黙的にinline
である。
問題点
[編集]インライン展開にまつわる問題だけでなく、インライン関数は言語機能として積極的に使用されない一面がある。その理由は次の通りである:
- 多くの場合、人間よりもコンパイラがインライン化すべき関数を決定する方がよい結果になる。人間がインライン化したいと考える関数よりもコンパイラがインライン化できると判断する関数の数が少ない場合は特にそうである。
- プログラムの修正によって、かつてはインライン化すべきだった関数がインライン化すべきでないものになったり、逆の変化が起きたりする。これはマクロの場合も似たようなものだが、コードの保守という観点から見ればインライン関数にはあまり利点はない。
- C言語でインライン関数を多用するとコンパイル時間が延びる傾向がある。これは関数の実体が呼び出している各所に埋め込まれて中間表現を形成するためである。コードサイズの増加はコンパイル時間の増加ももたらす。
インライン展開そのものの問題についてはインライン展開の欠点を参照されたい。
脚注
[編集]注釈
[編集]出典
[編集]- ^ Inline Functions (C++) | Microsoft Docs, The
inline
keyword is available only in C++. - ^ Inline Functions | Microsoft Docs
- ^ Java仮想マシン・ガイド §ディレクティブの記述 | Oracle Java SE 11 Help Center
- ^ inline 指定子 - cppreference.com
- ^ デフォルトコンストラクタ - cppreference.com
- ^ デストラクタ - cppreference.com
- ^ constexpr 指定子 (C++11以上) - cppreference.com