La raison en est que les lambdas sont des objets de fonction, donc les transmettre à un modèle de fonction instanciera une nouvelle fonction spécifiquement pour cet objet. Le compilateur peut ainsi intégrer de manière triviale l'appel lambda.
Pour les fonctions, en revanche, l'ancienne mise en garde s'applique: un pointeur de fonction est passé au modèle de fonction, et les compilateurs ont traditionnellement beaucoup de problèmes pour insérer des appels via des pointeurs de fonction. Ils peuvent théoriquement être insérés, mais uniquement si la fonction environnante est également incorporée.
À titre d'exemple, considérons le modèle de fonction suivant:
template <typename Iter, typename F>
void map(Iter begin, Iter end, F f) {
for (; begin != end; ++begin)
*begin = f(*begin);
}
Appelez-le avec un lambda comme ceci:
int a[] = { 1, 2, 3, 4 };
map(begin(a), end(a), [](int n) { return n * 2; });
Résultats dans cette instanciation (créée par le compilateur):
template <>
void map<int*, _some_lambda_type>(int* begin, int* end, _some_lambda_type f) {
for (; begin != end; ++begin)
*begin = f.operator()(*begin);
}
… Le compilateur sait _some_lambda_type::operator ()
et peut les appeler en ligne de manière triviale. (Et appeler la fonction map
avec n'importe quel autre lambda créerait une nouvelle instanciation de map
puisque chaque lambda a un type distinct.)
Mais lorsqu'elle est appelée avec un pointeur de fonction, l'instanciation se présente comme suit:
template <>
void map<int*, int (*)(int)>(int* begin, int* end, int (*f)(int)) {
for (; begin != end; ++begin)
*begin = f(*begin);
}
… Et f
pointe ici vers une adresse différente pour chaque appel à map
et donc le compilateur ne peut pas appeler en ligne à f
moins que l'appel environnant à map
n'ait également été inséré afin que le compilateur puisse résoudre f
une fonction spécifique.