编译器如何以不同于常规函数的方式处理lambdas?

Lou*_*tra 5 c++ lambda compilation language-lawyer

我想知道编译器如何处理lambda函数而不是常规函数.即使不包括捕获列表,正如我认为的那样,它似乎表现得略有不同.

例如,正如我在上一篇文章中所使用的那样,尝试传递constexpr lambda并使用它来显式指定返回类型,我使用了constexpr lambda并将其作为常规函数参数传递.我引用了部分答案.

constexpr函数的参数本身不是constexpr对象 - 因此您不能在常量表达式中使用它们.

template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) {
    return std::array<event, (l())>{};
} 
// Compiles with GCC (C++17), though ill-formed (according to the answer of my last post)
Run Code Online (Sandbox Code Playgroud)

虽然,引起我的注意的是,它确实编译了传递constexpr lambda,但是没有编译通过constexpr常规(全局)函数.是什么导致这种差异?编译器在常规函数和lambda函数之间是否存在其他行为差异?

编辑:Lambda实现的示例

foo([](){ return 4; }); // C++17 Lambda's are implicitly constexpr
Run Code Online (Sandbox Code Playgroud)

在这种情况下,lambda基本上是值的包装器.

编辑:全局功能

每当传递一个全局函数时,编译器会抱怨 - 而不是使用lambda - 该函数,无论是否定义为constexpr,都不能在常量条件下使用:

prog.cc:8:20: error: 'l' is not a constant expression
Run Code Online (Sandbox Code Playgroud)

Pas*_* By 1

从您的代码片段中删除一些无关的内容,我们得到

template<typename T>
constexpr void foo(T t)
{
    constexpr int i = t();
}

constexpr int f() { return 42; }
auto l = []{ return 42; }
Run Code Online (Sandbox Code Playgroud)

评论:

  1. 您正在尝试用作t()inside constexpr。事实上可能是一个正常的功能并且仍然表现相同。foofoo

  2. lambda不是函数。它是一个带有operator().

    struct L { constexpr int operator()() const { return 42; } };
    
    Run Code Online (Sandbox Code Playgroud)

    TL被推导为与调用中等效的类型foo(l)

  3. Tint(*)()与调用中一样推导出来foo(f)

  4. 在这两种情况下,t 都不是中的 constexpr foo


来自[expr.const]

表达式e是核心常量表达式e,除非 的求值遵循抽象机的规则,将求值以下表达式之一:[...]

  • 左值到右值的转换,除非 [...]

    • 一个非易失性泛左值,它引用用 constexpr 定义的非易失性对象,或者引用此类对象的非可变子对象,或者

    • 文字类型的非易失性泛左值,引用其生命周期开始于 的求值内的非易失性对象e

通过 a 调用int(*)()需要进行左值到右值的转换。t不是用 定义的对象constexpr,它的生命周期也不是在 的求值中开始的t()。因此t()不是constexprin foo(f)

调用operator()不需要左值到右值的转换。由于没有成员访问,因此它只是一个constexpr函数调用。因此t()是 aconstexprfoo(l).

从核心常量表达式到常量表达式还多了一步,但对于这里的讨论来说并不重要。