关于c ++:为什么lambdas可以通过编译器比普通函数更好地优化?

Why can lambdas be better optimized by the compiler than plain functions?

在他的书中,The C++ Standard Library (Second Edition)Nicolai Josuttis指出,与普通函数相比,编译器可以更好地优化lambdas。

In addition, C++ compilers optimize lambdas better than they do
ordinary functions.
(Page 213)

为什么会这样?

我想,当谈到内衬时,不应该再有任何区别了。我能想到的唯一原因是编译器可能与lambda具有更好的本地上下文,这样可以做出更多假设并执行更多优化。


原因是lambda是函数对象,因此将它们传递给函数模板将为该对象实例化一个新函数。因此,编译器可以非常简单地内联lambda调用。

另一方面,对于函数,旧的警告适用:函数指针被传递到函数模板,而编译器传统上通过函数指针在内联调用时有很多问题。它们理论上是内联的,但前提是周围的函数也是内联的。

例如,考虑以下函数模板:

1
2
3
4
5
template <typename Iter, typename F>
void map(Iter begin, Iter end, F f) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}

用这样的lambda调用它:

1
2
int a[] = { 1, 2, 3, 4 };
map(begin(a), end(a), [](int n) { return n * 2; });

此实例化的结果(由编译器创建):

1
2
3
4
5
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都有不同的类型。)

但是当使用函数指针调用时,实例化看起来如下:

1
2
3
4
5
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解析为一个特定的函数。


因为当你将一个"函数"传递给一个算法时,你实际上是在传递一个指向函数的指针,所以它必须通过指向函数的指针进行间接调用。当使用lambda时,正在将对象传递给为该类型专门实例化的模板实例,而对lambda函数的调用是直接调用,而不是通过函数指针进行的调用,因此更可能是内联的。