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