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

inf*_*inf 167 c++ optimization lambda compiler-optimization c++11

The C++ Standard Library (Second Edition)Nicolai Josuttis 在其着作中指出,编译器可以比普通函数更好地优化lambdas.

此外,C++编译器比普通函数更好地优化lambdas.(第213页)

这是为什么?

我认为在内联时不应该有任何差别.我能想到的唯一原因是编译器可能有一个更好的本地上下文与lambdas,这样可以做出更多假设并执行更多优化.

Kon*_*lph 169

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

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

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

template <typename Iter, typename F>
void map(Iter begin, Iter end, F f) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}
Run Code Online (Sandbox Code Playgroud)

用这样的lambda调用它:

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

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

template <>
void map<int*, _some_lambda_type>(int* begin, int* end, _some_lambda_type f) {
    for (; begin != end; ++begin)
        *begin = f.operator()(*begin);
}
Run Code Online (Sandbox Code Playgroud)

...编译器知道_some_lambda_type::operator ()并且可以简单地内联调用它.(并且map使用任何其他lambda 调用该函数将创建一个新的实例化,map因为每个lambda具有不同的类型.)

但是当使用函数指针调用时,实例化如下所示:

template <>
void map<int*, int (*)(int)>(int* begin, int* end, int (*f)(int)) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}
Run Code Online (Sandbox Code Playgroud)

...并且这里f指向每次调用的不同地址map,因此编译器不能内联调用,f除非周围的调用map也已内联,以便编译器可以解析f为一个特定的函数.

  • 也许值得一提的是,使用不同的lambda表达式实例化相同的函数模板将创建一个具有唯一类型的全新函数,这可能是一个缺点. (4认同)
  • @greggo绝对.问题是当处理无法内联的函数时(因为它们太大).在这里,对于回调的调用可以*仍然*在lambda的情况下内联,但不是在函数指针的情况下.`std :: sort`是使用lambdas而不是函数指针的经典示例,这里提高了七倍(可能更多,但我没有数据!)性能提升. (2认同)
  • @greggo我知道.这实际上就是我答案的最后一句话. (2认同)

jco*_*der 25

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

  • "对lambda函数的调用是直接调用" - 的确如此.**所有**函数对象都是如此,而不仅仅是lambdas.它只是函数指针,如果有的话,不能轻易内联. (4认同)