Lý do là lambdas là các đối tượng chức năng nên việc chuyển chúng vào một mẫu hàm sẽ khởi tạo một chức năng mới dành riêng cho đối tượng đó. Do đó, trình biên dịch có thể nội tuyến một cách tầm thường cuộc gọi lambda.
Mặt khác, đối với các hàm, cảnh báo cũ được áp dụng: một con trỏ hàm được chuyển đến mẫu hàm và các trình biên dịch theo truyền thống có rất nhiều vấn đề khi thực hiện các cuộc gọi thông qua các con trỏ hàm. Về mặt lý thuyết chúng có thể được nội tuyến, nhưng chỉ khi chức năng xung quanh cũng được nội tuyến.
Ví dụ, xem xét mẫu hàm sau:
template <typename Iter, typename F>
void map(Iter begin, Iter end, F f) {
for (; begin != end; ++begin)
*begin = f(*begin);
}
Gọi nó bằng lambda như thế này:
int a[] = { 1, 2, 3, 4 };
map(begin(a), end(a), [](int n) { return n * 2; });
Kết quả trong phần khởi tạo này (được tạo bởi trình biên dịch):
template <>
void map<int*, _some_lambda_type>(int* begin, int* end, _some_lambda_type f) {
for (; begin != end; ++begin)
*begin = f.operator()(*begin);
}
Trình biên dịch biết _some_lambda_type::operator ()
và có thể gọi nội tuyến một cách tầm thường. (Và việc gọi hàm map
với bất kỳ lambda nào khác sẽ tạo ra một khởi tạo mới map
vì mỗi lambda có một loại riêng biệt.)
Nhưng khi được gọi bằng một con trỏ hàm, phần khởi tạo trông như sau:
template <>
void map<int*, int (*)(int)>(int* begin, int* end, int (*f)(int)) {
for (; begin != end; ++begin)
*begin = f(*begin);
}
Càng và ở đây f
chỉ đến một địa chỉ khác nhau cho mỗi cuộc gọi map
và do đó trình biên dịch không thể gọi nội tuyến f
trừ khi cuộc gọi xung quanh map
cũng đã được nội tuyến để trình biên dịch có thể phân giải f
thành một chức năng cụ thể.