Причина в том, что лямбды являются объектами функций, поэтому передача их в шаблон функции создаст новую функцию специально для этого объекта. Таким образом, компилятор может тривиально включить лямбда-вызов.
С другой стороны, к функциям применяется старое предостережение: указатель на функцию передается в шаблон функции, и у компиляторов традиционно возникает много проблем с встраиванием вызовов через указатели на функции. Теоретически они могут быть встроенными, но только если встроенная функция также встроена.
В качестве примера рассмотрим следующий шаблон функции:
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одну конкретную функцию.