首页 文章

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

提问于
浏览
154

Nicolai Josuttis在他的书中指出,lambdas可以通过编译器比普通函数更好地进行优化 .

此外,C编译器优于lambdas优于普通函数 . (第213页)

这是为什么?

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

2 回答

  • 159

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

  • 24

    原因是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具有不同的类型 . )

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

    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 解析为一个特定函数 .

相关问题