编译器如何对待lambda与常规函数不同?

时间:2019-01-27 11:11:30

标签: c++ lambda compilation language-lawyer

我想知道编译器如何对待lambda函数而不是常规函数。我认为它甚至不包括捕获列表,它的行为似乎也略有不同。

例如,在上一篇文章Trying to pass a constexpr lambda and use it to explicitly specify returning type中,我使用了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)

尽管,引起我注意的是,这确实通过constexpr lambda进行编译,但是没有通过constexpr常规(全局)函数进行编译。是什么原因导致这种差异? 常规函数和lambda函数之间的编译器行为是否还有其他区别?

编辑:Lambda实现示例

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

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

编辑:全局功能

每当传递全局函数时,与使用lambda相对,编译器都会抱怨该函数(无论是否定义为constexpr)都不能在恒定条件下使用:

prog.cc:8:20: error: 'l' is not a constant expression

1 个答案:

答案 0 :(得分:1)

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

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

constexpr int f() { return 42; }
auto l = []{ return 42; }

备注:

  1. 您正在尝试将t()内的constexpr用作foofoo实际上可以是一个正常函数,并且仍然具有相同的功能。

  2. lambda 不是一个函数。这是带有operator()的匿名结构。

    struct L { constexpr int operator()() const { return 42; } };
    

    T被推导为与呼叫L中的foo(l)等效的类型。

  3. T在呼叫int(*)()中推导为foo(f)

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


来自[expr.const]

  

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

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

         
        
    • 一个非易失性glvalue,它引用用constexpr定义的非易失性对象,或者引用该对象的一个​​非可变子对象,或者

    •   
    • 文字类型的非易失性glvalue,指的是其生命周期始于e的非易失性对象;

    •   
  •   

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

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

从核心常量表达式到常量表达式都有one more step,但是对于此处的讨论并不重要。