La razón es que las lambdas son objetos de función, por lo que pasarlas a una plantilla de función creará una nueva función específicamente para ese objeto. De este modo, el compilador puede en línea trivialmente la llamada lambda.
Para las funciones, por otro lado, se aplica la advertencia anterior: se pasa un puntero de función a la plantilla de función, y los compiladores tradicionalmente tienen muchos problemas para alinear llamadas a través de punteros de función. Teóricamente pueden estar en línea, pero solo si la función circundante también está en línea.
Como ejemplo, considere la siguiente plantilla de función:
template <typename Iter, typename F>
void map(Iter begin, Iter end, F f) {
for (; begin != end; ++begin)
*begin = f(*begin);
}
Llamándolo con una lambda como esta:
int a[] = { 1, 2, 3, 4 };
map(begin(a), end(a), [](int n) { return n * 2; });
Resultados en esta instanciación (creada por el compilador):
template <>
void map<int*, _some_lambda_type>(int* begin, int* end, _some_lambda_type f) {
for (; begin != end; ++begin)
*begin = f.operator()(*begin);
}
... el compilador sabe _some_lambda_type::operator ()
y puede hacer llamadas en línea de manera trivial. (E invocar la función map
con cualquier otra lambda crearía una nueva instancia de map
ya que cada lambda tiene un tipo distinto).
Pero cuando se llama con un puntero de función, la instanciación se ve de la siguiente manera:
template <>
void map<int*, int (*)(int)>(int* begin, int* end, int (*f)(int)) {
for (; begin != end; ++begin)
*begin = f(*begin);
}
… Y aquí f
apunta a una dirección diferente para cada llamada map
y, por lo tanto, el compilador no puede hacer llamadas en línea a f
menos que la llamada a map
también se haya en línea para que el compilador pueda resolver f
una función específica.