その理由は、ラムダは関数オブジェクトであるため、それらを関数テンプレートに渡すと、そのオブジェクト専用の新しい関数がインスタンス化されるためです。したがって、コンパイラはラムダ呼び出しを簡単にインライン化できます。
一方、関数については、古い警告が適用されます。関数ポインターは関数テンプレートに渡され、コンパイラーは従来、関数ポインターを介して呼び出しをインライン化する際に多くの問題を抱えていました。理論的にはインライン化できますが、周囲の関数もインライン化されている場合に限ります。
例として、次の関数テンプレートを考えてみましょう。
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
ことはできません。f
map
f