関数オブジェクトに関するウィキペディアの記事では、コンパイラーがそれらを「インライン化」できるため、for_eachとともに使用すると、そのようなオブジェクトにはパフォーマンス上の利点があると述べています。
私は、これがこの文脈で何を意味するのか、または私が言うのが恥ずかしい文脈で、少し曇っています。助けてくれてありがとう!
関数オブジェクトに関するウィキペディアの記事では、コンパイラーがそれらを「インライン化」できるため、for_eachとともに使用すると、そのようなオブジェクトにはパフォーマンス上の利点があると述べています。
私は、これがこの文脈で何を意味するのか、または私が言うのが恥ずかしい文脈で、少し曇っています。助けてくれてありがとう!
for_each
テンプレートの最後のパラメーターはファンクターです。ファンクター()
は、演算子を使用して(おそらく引数を使用して)「呼び出す」ことができるものです。定義上、ファンクターには2つの特徴的な種類があります。
()
(いわゆる関数オブジェクト)もファンクターです。さて、通常の関数をのファンクターとして使用したい場合はfor_each
、次のようになります。
inline void do_something(int &i) { /* do something */ }
int main() {
int array[10];
std::for_each(array, array + 10, &do_something);
}
この場合、for_each
テンプレートは[推定]引数を使用してインスタンス化されます<int *, void (*)(int &)>
。この場合の実際のファンクター値は&do_something
、関数の引数として渡される関数ポインターであることに注意してください。関数の観点からは、for_each
これは実行時の値です。また、これは実行時の値であるため、ファンクターへの呼び出しをインライン化することはできません。(一般的な場合と同様に、関数ポインターを介して行われた呼び出しをインライン化することは不可能です)。
ただし、代わりに関数オブジェクトを使用すると、コードは次のようになります。
struct do_something {
void operator()(int &i) { /* do something */ }
};
int main() {
int array[10];
std::for_each(array, array + 10, do_something());
}
この場合、for_each
テンプレートは[推定]引数を使用してインスタンス化されます<int *, do_something>
。内部からファンクターへの呼び出しはに転送されfor_each
ますdo_something::operator()
。呼び出しのターゲットは既知であり、コンパイル時に修正されます。ターゲット関数はコンパイル時に既知であるため、呼び出しを簡単にインライン化できます。
後者の場合、もちろん、引数として実行時の値が渡されますfor_each
。do_something
これは、を呼び出すときに作成するクラスの[おそらく「ダミー」の一時的な]インスタンスですfor_each
。ただし、この実行時の値は(operator ()
仮想でない限り)呼び出しのターゲットには影響しないため、インライン化には影響しません。
インラインは、コンパイラーが関数の呼び出しを関数自体の内容に置き換えることができるプロセスです。コンパイル時に関数の内容を知る必要があります。
関数ポインタが渡された場合、コンパイラはこれを実行できないことがよくあります。
インライン化とは、その関数へのすべての呼び出しをその関数の本体に直接置き換えることを意味します。
これは、新しい関数にジャンプしてから戻るオーバーヘッドを削減するため、小さな関数の最適化です。
これは、関数の定義(コード)がコピーされ、関数呼び出し(一部のシステムではコストがかかると考えられている)からあなたを救うことができることを意味します。マクロの置き換えを考えてください。