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