188

The C++ Standard Library (Second Edition)Nicolai Josuttisは、彼の著書の中で、ラムダはプレーン関数よりもコンパイラーによって最適化できると述べています。

さらに、C ++コンパイラは、通常の関数よりもラムダを最適化します。(213ページ)

何故ですか?

インライン化に関しては、これ以上違いはないはずだと思いました。私が考えることができる唯一の理由は、コンパイラーがラムダを使用したより良いローカルコンテキストを持っている可能性があり、そのようなものがより多くの仮定を行い、より多くの最適化を実行できることです。

4

3 に答える 3

193

その理由は、ラムダは関数オブジェクトであるため、それらを関数テンプレートに渡すと、そのオブジェクト専用の新しい関数がインスタンス化されるためです。したがって、コンパイラはラムダ呼び出しを簡単にインライン化できます。

一方、関数については、古い警告が適用されます。関数ポインターは関数テンプレートに渡され、コンパイラーは従来、関数ポインターを介して呼び出しをインライン化する際に多くの問題を抱えていました。理論的にはインライン化できますが、周囲の関数もインライン化されている場合に限ります。

例として、次の関数テンプレートを考えてみましょう。

template <typename Iter, typename F>
void map(Iter begin, Iter end, F f) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}

次のようなラムダで呼び出します。

int a[] = { 1, 2, 3, 4 };
map(begin(a), end(a), [](int n) { return n * 2; });

このインスタンス化の結果 (コンパイラによって作成されます):

template <>
void map<int*, _some_lambda_type>(int* begin, int* end, _some_lambda_type f) {
    for (; begin != end; ++begin)
        *begin = f.operator()(*begin);
}

… コンパイラはそれを知って_some_lambda_type::operator ()いて、簡単に呼び出しをインライン化できます。(そして、他のラムダで関数を呼び出すmap、各ラムダには異なる型があるため、 の新しいインスタンス化が作成されます。)map

しかし、関数ポインターで呼び出された場合、インスタンス化は次のようになります。

template <>
void map<int*, int (*)(int)>(int* begin, int* end, int (*f)(int)) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}

…そして、ここでfは への呼び出しごとに異なるアドレスを指しているため、コンパイラーは への呼び出しをインライン化するmapことはできません。fmapf

于 2012-12-05T11:43:10.513 に答える
28

「関数」をアルゴリズムに渡すと、実際には関数へのポインターを渡すため、関数へのポインターを介して間接呼び出しを行う必要があります。ラムダを使用する場合、その型用に特別にインスタンス化されたテンプレート インスタンスにオブジェクトを渡します。ラムダ関数の呼び出しは、関数ポインターを介した呼び出しではなく、直接呼び出しであるため、インライン化される可能性が高くなります。

于 2012-12-05T11:44:26.997 に答える