O motivo é que lambdas são objetos de função, portanto, passá-los para um modelo de função instancia uma nova função especificamente para esse objeto. O compilador pode, assim, alinhar trivialmente a chamada lambda.
Por outro lado, para funções, aplica-se a antiga advertência: um ponteiro de função é passado para o modelo de função, e os compiladores tradicionalmente têm muitos problemas em incluir chamadas por meio de ponteiros de função. Teoricamente, eles podem ser embutidos, mas apenas se a função circundante também estiver embutida.
Como exemplo, considere o seguinte modelo de função:
template <typename Iter, typename F>
void map(Iter begin, Iter end, F f) {
for (; begin != end; ++begin)
*begin = f(*begin);
}
Chamando-o com um lambda como este:
int a[] = { 1, 2, 3, 4 };
map(begin(a), end(a), [](int n) { return n * 2; });
Resultados nesta instanciação (criada pelo compilador):
template <>
void map<int*, _some_lambda_type>(int* begin, int* end, _some_lambda_type f) {
for (; begin != end; ++begin)
*begin = f.operator()(*begin);
}
… O compilador sabe _some_lambda_type::operator ()
e pode fazer chamadas em linha para ele trivialmente. (E invocar a função map
com qualquer outro lambda criaria uma nova instanciação, map
pois cada lambda tem um tipo distinto.)
Mas quando chamada com um ponteiro de função, a instanciação é a seguinte:
template <>
void map<int*, int (*)(int)>(int* begin, int* end, int (*f)(int)) {
for (; begin != end; ++begin)
*begin = f(*begin);
}
... e aqui f
aponta para um endereço diferente para cada chamada map
e, portanto, o compilador não pode incorporar chamadas para f
, a menos que a chamada ao redor map
também tenha sido incorporada para que o compilador possa resolver f
uma função específica.