Nicolai Josuttis在他的书中指出,lambdas可以通过编译器比普通函数更好地进行优化 .
此外,C编译器优于lambdas优于普通函数 . (第213页)
这是为什么?
我认为在内联时不应该有任何差别 . 我能想到的唯一原因是编译器可能有一个更好的本地上下文与lambdas,这样可以做出更多假设并执行更多优化 .
因为当你将“函数”传递给算法时,你实际上是在传递一个指向函数的指针,所以它必须通过指向函数的指针进行间接调用 . 当您使用lambda时,您将对象传递给专门为该类型实例化的模板实例,并且对lambda函数的调用是直接调用,而不是通过函数指针调用,因此更可能被内联 .
原因是lambdas是函数对象,因此将它们传递给函数模板将实例化一个专门用于该对象的新函数 . 因此编译器可以简单地内联lambda调用 .
另一方面,对于函数,旧的警告适用:函数指针被传递给函数模板,并且编译器传统上在通过函数指针进行调用时遇到很多问题 . 理论上它们可以内联,但前提是周围的函数也是内联的 .
例如,请考虑以下函数模板:
template <typename Iter, typename F> void map(Iter begin, Iter end, F f) { for (; begin != end; ++begin) *begin = f(*begin); }
用这样的lambda调用它:
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 () 并且可以简单地内联调用它 . (并且使用任何其他lambda调用函数 map 将创建 map 的新实例化,因为每个lambda具有不同的类型 . )
_some_lambda_type::operator ()
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 解析为一个特定函数 .
f
2 回答
因为当你将“函数”传递给算法时,你实际上是在传递一个指向函数的指针,所以它必须通过指向函数的指针进行间接调用 . 当您使用lambda时,您将对象传递给专门为该类型实例化的模板实例,并且对lambda函数的调用是直接调用,而不是通过函数指针调用,因此更可能被内联 .
原因是lambdas是函数对象,因此将它们传递给函数模板将实例化一个专门用于该对象的新函数 . 因此编译器可以简单地内联lambda调用 .
另一方面,对于函数,旧的警告适用:函数指针被传递给函数模板,并且编译器传统上在通过函数指针进行调用时遇到很多问题 . 理论上它们可以内联,但前提是周围的函数也是内联的 .
例如,请考虑以下函数模板:
用这样的lambda调用它:
此实例化中的结果(由编译器创建):
...编译器知道
_some_lambda_type::operator ()
并且可以简单地内联调用它 . (并且使用任何其他lambda调用函数map
将创建map
的新实例化,因为每个lambda具有不同的类型 . )但是当使用函数指针调用时,实例化如下所示:
...这里
f
指向每次调用map
的不同地址,因此编译器不能内联对f
的调用,除非对map
的周围调用也已内联,以便编译器可以将f
解析为一个特定函数 .