自执行 C++11 lambda 的成本

Cost of self-executing C++11 lambdas

window procedure 之外,我正在使用自执行的 lambda 编写 switch 语句,如下所示:

LRESULT CALLBACK proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
        case WM_CREATE: return [&](WPARAM wp, LPARAM lp) {
            do_something(wp, lp);
            return 0;
        }(wp, lp);

        case WM_SIZE: return [&](HWND hWnd) {
            do_another_thing(hWnd);
            return 0;
        }(hWnd);
    }
    return DefWindowProc(hWnd, msg, wp, lp);
}

我相信编译器可以按照他们想要的方式自由优化它,但一般来说,与不使用这些 lambda 相比,编译器会为此添加很多样板代码吗?

编译器能否检测到冗余的 lambda 并将其删除?

问题有点奇怪。编译器不会 "remove" lambda,因为 lambda 在您的 源代码 中,并且编译器不会修改您的源代码。它所做的是发出机器代码,产生您在源代码中表达的程序的行为。

编译器可以随意发出尽可能多或尽可能少的机器代码,只要结果按照您的程序表达的方式运行即可。

如果编译器可以将所有代码内联到一个地方,那么它当然不必在它们之间发出单独的函数体和jumps/calls,这是一个常见的应用优化。

像这样的优化问题没有明确的答案,因为兼容编译器的优化器可以做很多很多事情。然而,在这种情况下,大多数现代优化器几乎肯定会内联 lambda,并生成相同的程序集,无论您是否使用 lambda。因为 lambda 有一个独特的类型,编译器 可以 轻松内联。因为 lambda 被声明并立即使用并且从未被赋值(一个更常见的名称是 "immediately invoked/evaluated lambda instead of "self executing"),编译器知道它只能被调用一次。所以通常它会决定内联。

当然,您可以查看一些程序集:https://godbolt.org/g/QF6WmR。如您所见,此特定示例中生成的程序集是相同的,但显然不能证明一般情况。

一般来说,lambda 被认为是 C++ 中的低成本或零成本抽象,如果您认为 lambda 可以生成最干净的代码,那么请使用它。如果需要,您可以随时快速验证装配是否相同。不过,您以这种方式使用 lambda 的原因有点不寻常;我不会真的认为代码折叠是一个很好的理由。使用立即计算的 lambdas 的一个更常见的原因是能够在其他情况下不能使用 const

int x;
try {
    x = foo();
}
catch (const ExceptionType& e) {
    x = bar();
} 

const auto x = [] () {
    try {
        return foo();
    }
    catch (const ExceptionType& e) {
        return bar();
    }
}();

在传统的C++代码中,要在外部范围内持久化x,我们必须先声明它,然后再赋值给它。通过使用 returns 我们想要的值的 lambda,我们可以同时声明和分配 x,允许它是 const.