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